Ein Array (1D) größerer Ordnung schnell(er) durchkämmen

SparkMonkay

Commander
Registriert
Feb. 2013
Beiträge
2.337
Moinsen,

ich hänge hier vor einem Problem bezüglich meiner Konzeption einer Funktion. In erster Linie ist meine Frage mehr auf die Programmiersprache C gebunden, jedoch denke ich, dass mir solche Prinzipien/Verfahren auch in anderen Sprachen helfen könnten.

Erstmals eine Nebeninformation:
Dieses Programm arbeitet mit Daten, die eine unbestimmte Größe haben, daher geht es hier oder es sollte hier in diesem Thread um Array's der Größenordnung >500.000 Elemente gehen. Dieses Array soll NICHT sortiert werden, es sollte unverändert bleiben durch diesen Vorgang, die sind auch nicht sortiert, sondern die Informationen liegen wild im Array rum.

Zu der Frage:
Nehmen wir an ich laufe mein Array von mind. 500.000 Elementen ab. Und ich nehme mir einen Zähler, sprich das "übliche", und arbeite mich von vorne nach hinten durch.

Code:
int count=0;
int length=IRGENDWAS>0;

for(count=0; count<length ; count++)
{
//Das tun was verlangt ist oder suchen was gesucht ist
}

Jetzt würde ich gerne Wissen, wie viel Zeit meine Instruktionen brauchen und ob die Methode, dass ich das Array von vorne und hinten durchkämme schneller ist oder aufgrund der erhöhten Anzahl der Instruktionen die ich dann einsetzen muss genauso, wenn nicht sogar länger braucht.

Ich meine damit sowas dann:

Code:
int length =IRGENDWAS>0;
int count1=0;   //von vorne suchen
int count2=length-1; //von hinten suchen

while(count2>=count1)
{
//Das tun was verlangt ist oder suchen was gesucht ist

count1++;
count2--;
}

Bei diesem Fall müsste ich dann auch immer gucken welcher gewisse Bedingungen erfüllt und würde somit auf u.U die selbe Anzahl von Instruktionen kommen.

In Fall 1 habe ich eine Überprüfung pro Durchlauf.
In Fall 2 habe ich zwei Überprüfungen pro Durchlauf, dafür die hälfte an Durchläufen.

Wenn ich jetzt sagen würde, dass X die Anzahl meiner Durchläufe währe und Y die Anzahl meiner Instruktionen, dann würde ich bei Fall 1 folgendes bekommen: X*Y
In Fall 2 bekomme ich dann 2X*(Y/2)=X*Y. Das ist jetzt eigentlich ganz oberflächlich, da die Instruktionen unterschiedlich viel Zeit in Anspruch nehmen. Aber was davon wäre schneller?
Denn dieses Array muss leider mehrmals durchgearbeitet werden.


Danke für Hilfreiche Antworten!
Gruß SparkMonkay
 
das array vorwärts zu durchlaufen sollte wegen cache nutzung schneller sein als rückwärts zu durchlaufen.
 
array inn ein Temp array zwischenspeichern...

dieses sortieren und 2 dimensional machen, die 2te dimension den index von dem Orginalen "Index-platz" geben (damit du nachher das element im orginalen array wiederfindest (quasi eine "benotung"))..

nun haste mehrer möglichkeiten, thema sortieverfahren... qicksort, hashing etc..

damit hättest du ein logeritmisches verhalten und das beste laufzeitverhalten...

z.b. wenn es wirklich viele daten werden (in die mios. wirst du es deutlich merken, wenn du das array unsortiert durchsuchst)

Lg

p.s. dein vorschlag fürde LOG² entsprechen und nocht LOG X. = sehr schlchter ansatz^^
und mit der sortierung und bewertung musst du max. 2 mal durch ;=)
 
Zuletzt bearbeitet:
Gehen wir mal davon aus du möchtest etwas im Array finden:

Wenn du das ganze auf mehrere Threads aufteilen würdest, die dann jeweils nur Bruchteile des Arrays untersuchen, so dass es dann parallel läuft, muss das auf der Hardware gut skalieren UND die Threads müssen ja untereinander kommunizieren (einander abbrechen können).

Alleine so hast du die Komplexität stark gesteigert, somit fraglich ob performanter (kann aber schon sein).
Und den Speicherbedarf erhöht (das Ursprunges Array belegt Speicher und wird dann noch geteilt und erneut abgespeichert statt referenziert -> doppelter Speicherbedarf mindestens).

So wie du das Programm Nr. 2 geschrieben hast, ist es dann eher Fallabhängig ob dir das ganze hilft.
Wenn der Treffer (wir gehen immer noch von einer Suche aus) am Ende des Array ist, hast du nur wenige Schritte (oder nur einen benötigt).

Ansonsten, wie bereits erwähnt, es gibt diverse Sortier-Algorithmen etc.
 
Ein ganz pragmatischer Ansatz wäre jetzt, beide Varianten einfach mal zu testen ;)

Wie sieht es mit einem multithread-Ansatz aus?

Und hast du da wirklich ein klassisches Array oder ist das eher eine Struktur in der Form einer verketteten Liste? Eventuell ergeben sich da unterschiedlich anwendbare Ansätze... bin aber schon zu lange aus C raus.
 
also ohne sortierung hast ja immer einen suchaufwand o(n).
zu deiner frage zählst einfach die cpu zyklen (für den jeweiligen worst case). die ist bei beiden fällen identisch. beim zweiten fall hast ein int mehr speicherverbrauch.

wenn die ergebnisse der durchläufe voneinander unabhängig sind, kannst du mit multithreading optimieren.
zb. bei nem 4kern prozessor jeweils 1/4 des arrays bzw. speicherbereichs an einen thread geben.

bei komplexeren aufgaben als suchen hilft oft branch prediction, falls der compiler das nicht schon übernimmt. dadurch landen die richtigen befehle schon in der pipeline.
zb. wenn nur 1 element aus der riesigen liste das korrekte ist, dann ist ein miss wahrscheinlicher als ein hit.
 
Zuletzt bearbeitet:
Danke erstmals für die Antworten:

@striker159
nicht Rückwärts, sondern Vorwärts und Rückwärts und Vorwärts

@Musicon
Oder ein 1D Array wo ich der Reihe nach einfach die Indizes schreibe. Das Array welches ich habe ist ein "Zahlen" Array , ich verwende den Datentyp long, aufgrund dessen, dass es große Zahlen sein können.

Gut dass du mich daran erinnert hast, das habe ich vergessen! Ich verlauf des Programms ändert sich die Größe der Elemente des Array's das würde bedeuten, dass ich jedes mal wenn ich das tun will, um z.B. das größte oder keinste Element zu finden, erneut Sortieren müsste. Dies würde den Aufwand dann erhöhen, ich bin mir dann unsicher ob ich das will.

Was meinst du mit Ansatz? Welcher? Einseitiges suchen oder beidseitiges suchen?

@Ghost
Threads würde ich auch benutzen, jedoch ist es in diesem Projekt nicht zugelassen. Außerdem muss ich immer das ganze Array durchlaufen, denn ich soll ja das größte/kleinste oder was auch immer Element finden.

@KillerCow
Ich würde es so machen. Das Programm nun folgendermaßen dann testen.
1. Zeit ausgeben (ja ich googel gleich wie das in C geht ;) )
2. Suche starten
3. Zeit ausgeben
4. Terminieren

Dann sollte ich wissen welche von den beiden Sachen so schneller ist.

@merv
Danke aber das sortieren stellt sich als zu Zeitaufwändig heraus. Ich suche und wende etwas darauf an. Dann suche ich erneut und wende etwas darauf an. Dadurch verändern sich die Werte der Elemente in dem Array und jedes anwenden nach der Suche würde somit erfordern, dass ich das Array erneut sortiere.

Die Links sind ganz nett, solche Seiten mit Code-Snippets haben mir auch bisher schöne Ideen gegeben.

@DonnyDeep
Die Ergebnisse sind in meinem Fall voneinander abhängig. Aber Multi-Threading ist leider nicht möglich...
 
Zuletzt bearbeitet:
Muss es denn unbedingt ein Array sein? Dein Vorhaben schreit nach dynamischeren Strukturen, wie Binärbäumen oder Heaps.

Ein bisschen was konkreteres wäre nebenbei auch nett. Was kann denn zum Beispiel jetzt mal ganz konkret mit einem Array passieren?

Threads würde ich auch benutzen, jedoch ist es in diesem Projekt zugelassen.
Bin ich blöd oder fehlt da ein "nicht"?

Bei Arrays in der Größenordnung und bei geringem Rechenaufwand pro Element ist der Schleifenoverhead nebenbei komplett zu vernachlässigen, da man viel eher speicherlimitiert ist. Multithreading würde da auch nur helfen, den Speichercontroller besser auszulasten, eine gute Skalierung wäre nicht zu erwarten.

Und wegen des wahrscheinlichen Speicherlimits ist auch die Variante, das Array einfach von vorne nach hinten durchzugehen, das Schnellste, was man machen kann - zwar kann jede x86-CPU der letzten 10 Jahre mehr als zwei Streams handhaben und auch "rückwärts" prefetchen, aber es bringt keinen Vorteil.

Je nach Anforderung und Elementtypen könnte man nebenbei explizit mit SIMD-Befehlen optimieren, sowie explizites Prefetching mittels PREFETCHNTA verwenden (bringt Performancevorteile, wenn die Daten nur 1x benutzt werden und/oder nicht in den CPU-Cache passen, weil ausschließlich der L1-Cache benutzt wird).
 
Zuletzt bearbeitet:
Einfach das sortierte array triggern, sobald ein eintrag reinkommt der nicht gewichtet ist = neusortieren ;)

es gint nen algo, der heißt merge sort. guck in dir mal an, denke das ist genau das was du brauchst ;)
 
@VikingGe
Ich habe nicht geahnt, dass es in dem Sinne so "nervig" wird. Ich müsste sonst 1000 Zeile Code umschreiben. Was mit dem Array passiert? Ich habe einen Haufen Zahlen. Diese Zahlen kommen in diesen Array. Eins nach dem anderen. Dadurch verändert sich die tatsächliche Größe dieses Array's. Dieser Haufen Zahlen kann auch eine unbestimmte Größe haben.

Ja, ich bin blöd nicht du, ich sollte wenn ich was hier poste noch ein drittes mal lesen. Danke dir, werde es korrigieren.

@Musicon
In Ordnung, jedoch würde es heißen, dass ich nach jeder Anweisung für die Einordnung in das Array (ein paar Zeilen hier drüber erwähnt), ich werde es mal versuchen. Bin bis jetzt leider nicht dazu gekommen. Ich würde mal sagen, dass mit Bubble Sort oder Quick-Sort das recht schnell sortiert werden sollte.
 
SparkMonkay schrieb:
Threads würde ich auch benutzen, jedoch ist es in diesem Projekt zugelassen. Außerdem muss ich immer das ganze Array durchlaufen, denn ich soll ja das größte/kleinste oder was auch immer Element finden.

Kannst du doch trotzdem in Threads aufteilen? Angenommen du hast 4 Kerne, dann Teilst du das Array in 4 Teile und jeder Thread sucht in seinem Teil nach dem größten/kleinsten/wasauchimmer Element und am Ende vergleichst du die Ergebnisse die jeder Thread gefunden hat miteinander um das insgesamt größte/kleinste/wasauchimmer Element zu bekommen.
 
Es kommt auch stark darauf an, was man da sortierst.
Sortierst man bspw. 500.000 Messdaten der Lufttemperatur, hast man netto vielleicht nur 500 echt verschiedene Werte, die es zu sortieren gilt, dann ist ein vergleichsbasiertes Sortierverfahren auf 500.000 Elementen die reinste Zeitverschwendung.
 
es gibt verschiedene arten von index,
die eine art wurde ja bereits genannt: btree index.

angenommen die werte kombination in den spalten ist nicht hoch (sowas geschlecht, oder land),
dann wäre eine bitmap index implementierung interessant.

im grunde erzeugst du für jede werte kombination ein bitmap,
wenn du nach einem wert suchst, durchläufst du dein vollen array machst aber mit bitmap vergleich.
Diese können von der cpu schneller verarbeitet werden als ein integer oder string opertor vergleich.
 
SparkMonkay schrieb:
Was mit dem Array passiert? Ich habe einen Haufen Zahlen. Diese Zahlen kommen in diesen Array. Eins nach dem anderen. Dadurch verändert sich die tatsächliche Größe dieses Array's.
Hört sich so an als würdest du selbst schrittweise Werte in das Array einfügen.
In diesem Fall könntest du einfach beim Einfügen jeweils eine min-/max-Variable aktualisieren.
Solange du nur einfügst, klappt das. Wenn du auch Elemente entfernen willst, dann klappts nur wenn keine Duplikate erlaubt sind.

Ansonsten solltest du evtl. zusätzlich zum Array (das du anscheinend nicht verändern darfst/willst) die Daten in passende Strukturen bringen (z.B. min-/max-Heap). Wie die Datenstrukturen dann genau aussehen sollten hängt von deinen Daten ab und was du genau damit anfangen willst.
 
@FreedomOfSpeech
Ich habe den Typo korrigiert.... ;)

@Grantig
Also mein Programm braucht insgesamt ca. 30. Sekunden um dass alles zu machen. Die Datei einlesen (ca. 1.5 Millionen Zeichen), das alles zu prüfen und seine Arbeit zu erledigen und dann am Ende eine Datei zu erstellen wo mein Resultat steht (auch mit ca. 1.5 Millionen Zeichen). Soweit habe ich herausgefunden, dass das durchkämmen von diesen Arrays richtig fix geht. Ich sollte mehr das lesen und überprüfen optimieren. Denn ich habe es mir zu einfach gemacht eine Methode zu schreiben, die mir Zeile X einliest und das zu umständlich. Wurde bereits gefixt. Zeitersparnis = 4.523 Sekunden ;)

Das Problem ist, dass ich die Zahlen die ich da reinsortiere auf verschiedene Arten da rein packen muss. Also mal nach besten Fall oder schlechtesten Fall, oder mal nach der normalen Reihenfolge, so wie es angelegt wurde.
Ein struct wäre das beste, dann kann ich es so gestalten, dass ich auf den nächsten in der Reihenfolge zeige, und mit einem anderen Pointer dann auf den nächst größeren. Das wäre der Idealfall, jedoch habe ich es schon gelöst. Nun ja, leider habe ich mich zuspät mit structs beschäftigt.
 
Es wäre natürlich hilfreich zu sehen, was "Das tun was verlangt ist oder suchen was gesucht ist" genau sein soll, wie viele Durchläufe du machen musst, wie die verschiedenen Aufgaben zusammenhängen und um was für eine architektur es geht, aber höchst wahrscheinlich ist das Sortieren oder Verwenden von komplexen Datenstrukturen wie B-Trees völlige Zeitverschwendung (sowohl Entwicklungszeit, als auch Laufzeit):

Das lineare Durchlaufen eines Array (also Version 1) ist so ziemlich das effizienteste, was eine CPU machen kann. Ein linearer Durchlauf über ein Array hat 0 Speicheroverhead, maximale Cache locality, ist für den Prefetcher am einfachsten zu erkennen und kann am einfachsten Vektorisiert werden. Auf Linked Lists oder B-Trees (das was du mit einen Structs erzeugen möchtest) trifft all das nicht zu.

Wo dir sortieren was bringt ist, wenn du z.B. Zahlen hast, diese nach Größe sortierst und dann nur noch fragen stellst, wie, "Was ist die größte/kleinste Zahl", "Was ist die kleinste Zahl größer X". Oder wenn du Daten hast und immer nur schaust, was in dem Datum zu einem bestimmten Schlüssell (z.B. Zeit oder Index) steht.

Was man auch bedenken sollte: Das Einlesen einer Datei ist EXTREM langsam, wenn diese von der Festplatte geladen werden muss. Sprich du kannst problemlos mehrere hundert bis Tausend Zyklen pro Datum darauf verwenden, diese in eine wie auch immer geartete Reihenfolge zu bringen - sofern dir diese Reihenfolge dann eben bei den Durchläufen auch einen Vorteil verschaft.

Falls du übrigens für x86 Programmierst, wird das Zählen von Instruktionen vermutlich nicht funktionieren:
a) Ohne in den generierten Assembler Code zu sehen hast du keine Chance abzuschätzen, wie viele und welche Instruktionen dein Code tatsächlich hat.
b) Verschiedene Instruktionen kosten unterschiedlich viel Zeit.
c) Gerade bei einfachen aufgaben ist es wesentlich wichtiger, deine Cache Misses zu minimieren, alls die Anzahl der Instruktionen zu minimieren.

Um das zu verdeutlichen: Ein voll ausgelasteter Haswell Kern könnte im Idealfall pro Cycle über 10 integer Operationen gleichzeitig durchführen. Wenn für eine Instruktion die Daten aber erst aus dem Hauptspeicher geladen werden müssen dauert es mehrere 100 Cycles bis diese Instruktion ausgeführt werden kann (von der Festplatte möchte ich erst garnicht anfangen). Im besten Fall gibt es andere Instruktionen, die der Kern so lange bearbeiten kann, im schlimmsten Fall steht die ganze Pipeline still und dieser eine Cache miss "kostet" dich mehrere 1000 Instruktionen (sind jetzt natürlich absolute worstcase Zahlen. In der Realität sind die Auswirkungen in der Regel wesentlich geringen).

Damit komme ich zu meinem letzten Hinweis: Wenn du wissen möchtest, welche von zwei Alternativen die schnellere ist, dann hilft nur messen.
 
@Miuwa, danke für deine Antwort

Ich habe das Programm auch fertig bekommen. Auch "ge-Benchmarkt". Es hat für eine Datei mit 500 Millionen Zeichen ca 3-4 Sekunden gebraucht. Vorher hatte es ca 30. Sekunden gebraucht. Aber das lag woanders. Ich habe es im Vergleich zu der Anforderung natürlich schon etwas auf die Spitze getrieben, denn das ist das 100 fache der Anforderungen die wir gehabt haben. Nur bin ich mir jetzt unsicher ob meine VM jetzt auf der HDD oder SSD lag und es wohl möglich an der SSD liegt ;D Dass muss ich heute Abend nachschauen.

11908067_1027083530665061_1478052578_n.jpg

Sogar weniger, fast 3 Sekunden. Auslesen und auf Fehler überprüfen hat vorher deutlich Länger gedauert. Aber mit dem linearen Array-Durchlauf ging es am schnellsten.

Aber danke nochmals an euch :)
 
Zurück
Oben