OK ich schätze da kommen wir dem ganzen Problem schon einmal deutlich näher:
1.) Der Code zum Generieren der Testdaten ist nicht ideal. Das kann man etwas eleganter lösen. Ich würde es ca. so schreiben ;-):
Code:
var Random = new Random();
var FrauenNamen = File.ReadAllText("Frauen.txt").Split(new String[]{"\n"},StringSplitOptions.RemoveEmptyEntries);
var MaennerNamen = File.ReadAllText("Maenner.txt").Split(new String[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
var StrassenNamen = File.ReadAllText("Strassen.txt").Split(new String[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
var Personen = new ArrayList();
foreach(String FrauenName in FrauenNamen)
{
foreach (String MaennerName in MaennerNamen)
{
foreach (String StrassenName in StrassenNamen)
{
int Alter = Random.Next(0, 80);
Personen.Add(new Person(FrauenName, MaennerName, StrassenName));
}
}
}
2.) Nachdem es darum geht, die Vorteile von LINQ aufzuzeigen, ist die Diskussion über Performanceoptimierungen schon einmal hinfällig.
3.) Ich kann mir schon gut vorstellen, warum es im Releasemodus funktioniert.
Grundsätzlich erzeugst du schon einmal viel zu viele Testdaten. Bei 500 Mio. Datensätzen zu je ca. 100 Zeichen würdest du schon 50GB an RAM verbraten. Ein 32Bit Prozess kann nur 2GB nutzen.
Der einzige Grund, warum es trotzdem funktioniert scheint darin zu liegen, dass so gut wie alle Strings doppelt sind. In .NET gibt es für Strings eine Optimierung, dass zwei gleiche Strings immer nur einmal im Speicher abgelegt werden. Das funktioniert deshalb, weil du einen String nicht bearbeiten kannst, sondern nur einen neuen erzeugen.
In deinem Fall brauchen also die Strings so gut wie keinen Platz. Das einzige, was Platz verbraucht ist die ArrayList von Personen.
Pro Person werden zumindest 3 32Bit Pointer auf den jeweiligen Stringwert benötigt, sowie 32Bit für das Alter = 16Byte + etwas Overhead für die Klasse an sich. Je nach Compilereinstellung kann der reale Platzverbrauch eine Klasse bzw. Struktur jedoch etwas schwanken. Bei C Compilern gibt es z.B. die Option die Strukturen auf ein Vielfaches von 16 Byte aufzufüllen. Vielleicht kommt gerade so eine Einstellung hier zu Tragen.
Ich würde an deiner Stelle die Menge der Testdaten etwas reduzieren. Ich denke 1 Mio. Datensätze sollten schon ein ausreichendes Bild abgeben. Dafür würde ich dafür sorgen, dass die Strings in deiner Klasse möglichst eindeutig sind d.h. an alle Frauen- bzw. Männernamen in jedem Schleifendurchlauf noch eine Zufallszahl hinten anhängen. Sonst hat das zur Folge, dass du real nur ein paar Strings verwendest und diese dann sehr schnell im Cache der jeweiligen Kerne landen, was das Bild durch sehr stark verzerrt. Bei großen Datenmengen sollte in erster Linie der RAM limitieren.
4.) Du solltest das Query auch mitstoppen. Vielleicht ist z.B. beim Parallel For schon ein extra Initialisierungsaufwand notwendig.
5.) Kannst du mir deine Testdateien einmal schicken. Dann kann ich schauen, wie groß der Unterschied bei mir ist (Sandy Bridge mit 4 Kernen/8 Threads) bzw. wie schnell ich den Code optimieren kann (bei deiner vorgegebenen ArrayList). Mich würde so ein Vergleich dringend interessieren, vor allem, wenn ich ein paar verschiedene CollectionTypen vergleichen kann (z.B. List(Of T) vs. ArrayList vs. Array)
6.) Kleine Frage an die Spezialisten, die aus dem Array gerne eine Datenbank machen wollen:
Habt ihr schon einmal einen Server für eine Datenbank mit 500 Mio. Datensätzen gesehen? Da tut es ein einfacher Bürorechner schon lange nicht meh. Wenn man da nicht mindestens RAM im dreistelligen GB Bereich reinstopft, ist da ziemlich bald Sense, einmal abgesehen davon ist der Performance beim Durchsuchen auch deutlich schlechter, selbst mit angepasstem Index, da es z.B. für name.contains("1") keinen Index gibt ;-)