do{}while(false); -- ein besseres goto?
-
"Ich sortiere mal ein Wenig um, dabei wird's leider die Kommentare zerhauen." "Zuerstmal sind diese ganzen gotos sinnlos. Und retVal auch." BOOL IE401IndexFile::EnumHashValues() { if ( m_pBuf == NULL) return FALSE; HASH_FILEMAP_ENTRY* pTable; HASH_ITEM* pItem; // pTable is located by an offset which is located at dwHashTableOffset pTable = (HASH_FILEMAP_ENTRY*)(m_pBuf + (((_MEMMAP_HEADER_SMALL*)m_pBuf)->dwHashTableOffset)); // The first location in the table follows immediately after the HASH_FILEMAP_ENTRY pTable pItem = (HASH_ITEM*)(pTable + 1); if (pTable->dwSig != SIG_HASH) return FALSE; do // Scan the list of tables. { // Scan the current table. for (; (LPBYTE)pItem < (LPBYTE)pTable + BYTES_PER_TABLE; pItem++) { // call virtual entry handler if ( HandleHashElement( pItem) == FALSE) return FALSE; } // Follow the link to the next table. if (!pTable->dwNext) { pTable = NULL; } else { // Validate the table signature and sequence number. DWORD nBlock; nBlock = pTable->nBlock; pTable = (HASH_FILEMAP_ENTRY*) (m_pBuf + pTable->dwNext); if (pTable->dwSig != SIG_HASH || pTable->nBlock != nBlock + 1) return FALSE; // Set pointer to first location in table. pItem = (HASH_ITEM*) (pTable + 1); } } while (pTable); return TRUE; }
"Ein paar Definitionen später machen." BOOL IE401IndexFile::EnumHashValues() { if ( m_pBuf == NULL) return FALSE; // pTable is located by an offset which is located at dwHashTableOffset HASH_FILEMAP_ENTRY* pTable = (HASH_FILEMAP_ENTRY*)(m_pBuf + (((_MEMMAP_HEADER_SMALL*)m_pBuf)->dwHashTableOffset)); if (pTable->dwSig != SIG_HASH) return FALSE; // The first location in the table follows immediately after the HASH_FILEMAP_ENTRY pTable HASH_ITEM* pItem = (HASH_ITEM*)(pTable + 1); do // Scan the list of tables. { // Scan the current table. for (; (LPBYTE)pItem < (LPBYTE)pTable + BYTES_PER_TABLE; pItem++) { // call virtual entry handler if ( HandleHashElement( pItem) == FALSE) return FALSE; } // Follow the link to the next table. if (!pTable->dwNext) { pTable = NULL; } else { // Validate the table signature and sequence number. DWORD nBlock; nBlock = pTable->nBlock; pTable = (HASH_FILEMAP_ENTRY*) (m_pBuf + pTable->dwNext); if (pTable->dwSig != SIG_HASH || pTable->nBlock != nBlock + 1) return FALSE; // Set pointer to first location in table. pItem = (HASH_ITEM*) (pTable + 1); } } while (pTable); return TRUE; }
"Verfummeln der Schleifenvariablen statt break sehe ich da." BOOL IE401IndexFile::EnumHashValues() { if ( m_pBuf == NULL) return FALSE; // pTable is located by an offset which is located at dwHashTableOffset HASH_FILEMAP_ENTRY* pTable = (HASH_FILEMAP_ENTRY*)(m_pBuf + (((_MEMMAP_HEADER_SMALL*)m_pBuf)->dwHashTableOffset)); if (pTable->dwSig != SIG_HASH) return FALSE; // The first location in the table follows immediately after the HASH_FILEMAP_ENTRY pTable HASH_ITEM* pItem = (HASH_ITEM*)(pTable + 1); do // Scan the list of tables. { // Scan the current table. for (; (LPBYTE)pItem < (LPBYTE)pTable + BYTES_PER_TABLE; pItem++) { // call virtual entry handler if ( HandleHashElement( pItem) == FALSE) return FALSE; } // Follow the link to the next table. if (!pTable->dwNext) break; // Validate the table signature and sequence number. DWORD nBlock; nBlock = pTable->nBlock; pTable = (HASH_FILEMAP_ENTRY*) (m_pBuf + pTable->dwNext); if (pTable->dwSig != SIG_HASH || pTable->nBlock != nBlock + 1) return FALSE; // Set pointer to first location in table. pItem = (HASH_ITEM*) (pTable + 1); } while (pTable); return TRUE; }
"Doppelter Code." BOOL IE401IndexFile::EnumHashValues() { if ( m_pBuf == NULL) return FALSE; // pTable is located by an offset which is located at dwHashTableOffset HASH_FILEMAP_ENTRY* pTable = (HASH_FILEMAP_ENTRY*)(m_pBuf + (((_MEMMAP_HEADER_SMALL*)m_pBuf)->dwHashTableOffset)); if (pTable->dwSig != SIG_HASH) return FALSE; // The first location in the table follows immediately after the HASH_FILEMAP_ENTRY pTable HASH_ITEM* pItem; do // Scan the list of tables. { pItem = (HASH_ITEM*) (pTable + 1); // Scan the current table. for (; (LPBYTE)pItem < (LPBYTE)pTable + BYTES_PER_TABLE; pItem++) { // call virtual entry handler if ( HandleHashElement( pItem) == FALSE) return FALSE; } // Follow the link to the next table. if (!pTable->dwNext) break; // Validate the table signature and sequence number. DWORD nBlock; nBlock = pTable->nBlock; pTable = (HASH_FILEMAP_ENTRY*) (m_pBuf + pTable->dwNext); if (pTable->dwSig != SIG_HASH || pTable->nBlock != nBlock + 1) return FALSE; } while (pTable); return TRUE; }
"Oh, dann kann pItem auch lokaler werden." BOOL IE401IndexFile::EnumHashValues() { if ( m_pBuf == NULL) return FALSE; // pTable is located by an offset which is located at dwHashTableOffset HASH_FILEMAP_ENTRY* pTable = (HASH_FILEMAP_ENTRY*)(m_pBuf + (((_MEMMAP_HEADER_SMALL*)m_pBuf)->dwHashTableOffset)); if (pTable->dwSig != SIG_HASH) return FALSE; do // Scan the list of tables. { // Scan the current table. for (HASH_ITEM* pItem=(HASH_ITEM*) (pTable + 1); (LPBYTE)pItem < (LPBYTE)pTable + BYTES_PER_TABLE; pItem++) { // call virtual entry handler if ( HandleHashElement( pItem) == FALSE) return FALSE; } // Follow the link to the next table. if (!pTable->dwNext) break; // Validate the table signature and sequence number. DWORD nBlock; nBlock = pTable->nBlock; pTable = (HASH_FILEMAP_ENTRY*) (m_pBuf + pTable->dwNext); if (pTable->dwSig != SIG_HASH || pTable->nBlock != nBlock + 1) return FALSE; } while (pTable); return TRUE; }
"Schon möchte die Schleife da raus." BOOL ScanTheCurrentTable(HASH_FILEMAP_ENTRY* pTable) { for (HASH_ITEM* pItem=(HASH_ITEM*) (pTable + 1); (LPBYTE)pItem < (LPBYTE)pTable + BYTES_PER_TABLE; pItem++) // call virtual entry handler if ( HandleHashElement( pItem) == FALSE) return FALSE; return TRUE; } BOOL IE401IndexFile::EnumHashValues() { if ( m_pBuf == NULL) return FALSE; // pTable is located by an offset which is located at dwHashTableOffset HASH_FILEMAP_ENTRY* pTable = (HASH_FILEMAP_ENTRY*)(m_pBuf + (((_MEMMAP_HEADER_SMALL*)m_pBuf)->dwHashTableOffset)); if (pTable->dwSig != SIG_HASH) return FALSE; do // Scan the list of tables. { if(!ScanTheCurrentTable(pTable)) return FALSE; // Follow the link to the next table. if (!pTable->dwNext) break; // Validate the table signature and sequence number. DWORD nBlock; nBlock = pTable->nBlock; pTable = (HASH_FILEMAP_ENTRY*) (m_pBuf + pTable->dwNext); if (pTable->dwSig != SIG_HASH || pTable->nBlock != nBlock + 1) return FALSE; } while (pTable); return TRUE; }
"Das break war auch ein return." BOOL ScanTheCurrentTable(HASH_FILEMAP_ENTRY* pTable) { for (HASH_ITEM* pItem=(HASH_ITEM*) (pTable + 1); (LPBYTE)pItem < (LPBYTE)pTable + BYTES_PER_TABLE; pItem++) // call virtual entry handler if ( HandleHashElement( pItem) == FALSE) return FALSE; return TRUE; } BOOL IE401IndexFile::EnumHashValues() { if ( m_pBuf == NULL) return FALSE; // pTable is located by an offset which is located at dwHashTableOffset HASH_FILEMAP_ENTRY* pTable = (HASH_FILEMAP_ENTRY*)(m_pBuf + (((_MEMMAP_HEADER_SMALL*)m_pBuf)->dwHashTableOffset)); if (pTable->dwSig != SIG_HASH) return FALSE; do // Scan the list of tables. { if(!ScanTheCurrentTable(pTable)) return FALSE; // Follow the link to the next table. if (!pTable->dwNext) return FALSE; // Validate the table signature and sequence number. DWORD nBlock = pTable->nBlock; pTable = (HASH_FILEMAP_ENTRY*) (m_pBuf + pTable->dwNext); if (pTable->dwSig != SIG_HASH || pTable->nBlock != nBlock + 1) return FALSE; } while (pTable); return TRUE; }
"Geht eigentlich alles locker von der Hand." "Mal nur zum Spaß wirr weitersuchen." "pTable->dwSig != SIG_HASH mal abtrennen." BOOL ScanTheCurrentTable(HASH_FILEMAP_ENTRY* pTable) { for (HASH_ITEM* pItem=(HASH_ITEM*) (pTable + 1); (LPBYTE)pItem < (LPBYTE)pTable + BYTES_PER_TABLE; pItem++) // call virtual entry handler if ( HandleHashElement( pItem) == FALSE) return FALSE; return TRUE; } BOOL IE401IndexFile::EnumHashValues() { if ( m_pBuf == NULL) return FALSE; // pTable is located by an offset which is located at dwHashTableOffset HASH_FILEMAP_ENTRY* pTable = (HASH_FILEMAP_ENTRY*)(m_pBuf + (((_MEMMAP_HEADER_SMALL*)m_pBuf)->dwHashTableOffset)); if (pTable->dwSig != SIG_HASH) return FALSE; do // Scan the list of tables. { if(!ScanTheCurrentTable(pTable)) return FALSE; // Follow the link to the next table. if (!pTable->dwNext) return FALSE; // Validate the table signature and sequence number. DWORD nBlock = pTable->nBlock; pTable = (HASH_FILEMAP_ENTRY*) (m_pBuf + pTable->dwNext); if (pTable->nBlock != nBlock + 1) return FALSE; if (pTable->dwSig != SIG_HASH) return FALSE; } while (pTable); return TRUE; }
"Und diese Duplikation auch wegmachen." BOOL ScanTheCurrentTable(HASH_FILEMAP_ENTRY* pTable) { for (HASH_ITEM* pItem=(HASH_ITEM*) (pTable + 1); (LPBYTE)pItem < (LPBYTE)pTable + BYTES_PER_TABLE; pItem++) // call virtual entry handler if ( HandleHashElement( pItem) == FALSE) return FALSE; return TRUE; } BOOL IE401IndexFile::EnumHashValues() { if ( m_pBuf == NULL) return FALSE; // pTable is located by an offset which is located at dwHashTableOffset HASH_FILEMAP_ENTRY* pTable = (HASH_FILEMAP_ENTRY*)(m_pBuf + (((_MEMMAP_HEADER_SMALL*)m_pBuf)->dwHashTableOffset)); do // Scan the list of tables. { if (pTable->dwSig != SIG_HASH) return FALSE; if(!ScanTheCurrentTable(pTable)) return FALSE; // Follow the link to the next table. if (!pTable->dwNext) return FALSE; // Validate the table signature and sequence number. DWORD nBlock = pTable->nBlock; pTable = (HASH_FILEMAP_ENTRY*) (m_pBuf + pTable->dwNext); if (pTable->nBlock != nBlock + 1) return FALSE; } while (pTable); return TRUE; }
"Bachte, daß das bloß Codeschubserei ist, die klappt, ohne," "daß ich überhaupt weiß, was der Code macht." "Deswegen auch der schlechte Name der ausgelagerten Schleife." "Den hab ich vom Kommentar geklaut." "Vielleicht ist sowas gemeint, keine Ahnung." BOOL IsTableFull(HASH_FILEMAP_ENTRY* pTable) { for (HASH_ITEM* pItem=(HASH_ITEM*) (pTable + 1); (LPBYTE)pItem < (LPBYTE)pTable + BYTES_PER_TABLE; pItem++) // call virtual entry handler if ( HandleHashElement( pItem) == FALSE) return FALSE; return TRUE; } BOOL IE401IndexFile::EnumHashValues() { if ( m_pBuf == NULL) return FALSE; // pTable is located by an offset which is located at dwHashTableOffset HASH_FILEMAP_ENTRY* pTable = (HASH_FILEMAP_ENTRY*)(m_pBuf + (((_MEMMAP_HEADER_SMALL*)m_pBuf)->dwHashTableOffset)); do // Scan the list of tables. { if (pTable->dwSig != SIG_HASH) return FALSE; if(!IsTableFull(pTable)) return FALSE; // Follow the link to the next table. if (!pTable->dwNext) return FALSE; // Validate the table signature and sequence number. DWORD nBlock = pTable->nBlock; pTable = (HASH_FILEMAP_ENTRY*) (m_pBuf + pTable->dwNext); if (pTable->nBlock != nBlock + 1) return FALSE; } while (pTable); return TRUE; }
"Vielleicht man nBlock wegmachen." "Nebenbei fange ich langsam an, den Code zu verstehen und kann versuchen," "die Kommentare zu heilen." BOOL IsTableFull(HASH_FILEMAP_ENTRY* pTable) { for (HASH_ITEM* pItem=(HASH_ITEM*) (pTable + 1); (LPBYTE)pItem < (LPBYTE)pTable + BYTES_PER_TABLE; pItem++) // call virtual entry handler if ( HandleHashElement( pItem) == FALSE) return FALSE; return TRUE; } BOOL IE401IndexFile::EnumHashValues() { if ( m_pBuf == NULL) return FALSE; // pTable is located by an offset which is located at dwHashTableOffset HASH_FILEMAP_ENTRY* pTable = (HASH_FILEMAP_ENTRY*)(m_pBuf + (((_MEMMAP_HEADER_SMALL*)m_pBuf)->dwHashTableOffset)); do // Scan the list of tables. { // Validate the table signature if (pTable->dwSig != SIG_HASH) return FALSE; if(!IsTableFull(pTable)) return FALSE; // Move to next table HASH_FILEMAP_ENTRY* oldTable = pTable; if (!pTable->dwNext) return FALSE; pTable = (HASH_FILEMAP_ENTRY*) (m_pBuf + pTable->dwNext); // Validate the table sequence number. if (pTable->nBlock != oldTable->nBlock + 1) return FALSE; } while (pTable); return TRUE; }
Du siehst, es ist nicht nur keine, aber auch nicht die geringste
Notwendigkeit für ein goto, darüberhinaus war der Code durchaus
ein wenig reparaturbedürftig. Außerdem fühlte sich der Code so
an, als sei es eigentlich C, das in eine Klasse gepresst wurde.
-
;fricky schrieb:
ach mist, das ist mir wirklich nicht aufgefallen. ausserdem fehlt beim label noch eine 'nachbehandlung', ohne sind die gotos ja auch leicht gegen returns zu ersetzen. soll ich noch was raussuchen oder lieber nicht?
Der Code ist für mich nicht leicht verständlich. Ich weiß ja nicht, was EnumHashValues tut. Nur eins weiß ich, die HashValues werden nicht Enumeriert. Wenn alle Bezeichner unverständlich sind und die Datenstrukturen auch unklar, kann ich evtl nichts umbasteln. Aber die Wahrscheinlichkeit ist sehr groß, daß C++-Code, der das gleiche tut, gar keinen Bedarf für ein goto hätte.
Ich behaupte nicht, daß goto immer schlecht wäre. Die Gründe, weshalb goto hier und da wegfliegt, sind immer andere, mal weil die Compiler stärker sind und inline können, mal RAII, mal RVO, mal dies mal das mal solches, mal nichtmal ein Sprachmittel, sondern neu entdeckter Stil, aber goto ist nie weit weg, und oft mein erster Ansatz, was zu leicht geschriebenen schwer zu lesendenm Code führt, den ich dann umbaue in erst mit Umbau geschriebenen leicht zu lesenden Code, zusammen ist das so unglaublich vielschichtig, daß man keine Beweisführung versuchen sollte, daß goto immer duch was besseres ersetzt werden kann. Ich kann nur sagen, daß ich goto anscheinend nicht brauche. Deswegen schaue ich immer, wenn ich goto geschrieben habe, ob ich nicht einen Denkfehler gemacht habe, der mich zum goto zwang, und ob nicht eine viel einfachere Ansicht des Problems zu einfacherem Code ohne goto führt. Irgendwie ist es immer so, seit vielen Jahren.
-
volkard schrieb:
Deswegen schaue ich immer, wenn ich goto geschrieben habe, ob ich nicht einen Denkfehler gemacht habe, der mich zum goto zwang, und ob nicht eine viel einfachere Ansicht des Problems zu einfacherem Code ohne goto führt.
kenne ich, ich hab z.b. was goto angeht sowas wie 'design patterns' im kopf. wenn ich mal goto verwenden will, dann dann läuft bei mir etwa dieses ab:
1. bekanntes, unproblematisches goto-muster (nested loops, cleanup, usw.): goto kommt rein, es wird nicht weiter drüber nachgedacht, hat noch nie gezickt oder zu verwirrung geführt.
2. leichte abweichung von (1): alarmglocke bimmelt, suboptimale programmstruktur wird angenommen und versucht des goto zu umgehen. wenn der code wider erwarten mit goto einfacher bleibt, wird goto eingesetzt.
3. starke abweichung von (1): ähnlich (2), resultiert aber so gut wie nie in einem goto, weil die funktion meistens schrottig programmiert war und komplett umgestellt werden muss.
-
Ich habe festgestellt, dass echte Programmierer mit Sprüngen in Assembler-Code klar kommen. Das heißt ohne, dass sie Ausschläge, Haarausfall, oder Sonstiges bekommen.
Dementsprechend kommen Programmierer auch mit gotos in Hochsprachen klar. Fricky's Snippet ist doch ein Bilderbuchbeispiel, denn jeder echte Programmierer kann den Code ohne Probleme lesen. Und das zählt, denn in der Realität werdet ihr meist keinen Bilderbuchcode zu Gesicht bekommen. Wer also nicht in der Lage ist auch sub-optimalen Code lesen zu können, der taugt als Programmierer einfach nichts (keine Sorge gibt genug Sparten wo es auch ohne geht: der Java, PHP und VB Markt ist groß).
Wichtig ist doch nur, dass eine Funktion keine zwei-stelligen Verschachtelungstiefen enthält. Selbst wenn eine Funktion mal 200 Zeilen lang ist so heißt das noch lange nicht, dass man nicht ohne größere Probleme nachvollziehen kann was sie tut.Am Ende ist es das was für mich zählt, nämlich, dass ich den Code ohne größere Hürden lesen kann. Und wenn ein Kollege dann mal eine 200 Zeilen-Funktion mit einem goto irgendwo hat, dann verteufel ich ihn deshalb nicht. Ich verwende zwar kein goto außerhalb von Cleanup-Code, aber beim schnellen prototyping einer Funktion wird die bei mir auch mal länglich (wobei tief-verschachtelte Schleifen meist Funktionalität bedeuten, was ich direkt auslagere, d.h. meine länglichen Funktionen sind meist nur viel boiler-plate Code; sprich fehlerüberprüfung und Aufrufen anderer Funktionen) und ich will oder kann sie nicht sofort zerkleinern, da noch gar nicht absehbar ist ob sie so bleibt. Und manchmal vergisst man es dann auch ganz einfach ...
Mir ist auch klar, dass es in dem Projekt, aus welchem fricky's Beispiel ist, der Code überall so oder so ähnlich aussehen wird (und nicht meinem Stil entspricht), aber Leseprobleme werde ich wohl kaum haben (Funktion passt auf einen Bildschirm und hat Kommentare - was will ich mehr!? <- rhetorische Frage!).
-
Echter Progger schrieb:
Dementsprechend kommen Programmierer auch mit gotos in Hochsprachen klar. Fricky's Snippet ist doch ein Bilderbuchbeispiel, denn jeder echte Programmierer kann den Code ohne Probleme lesen. Und das zählt, denn in der Realität werdet ihr meist keinen Bilderbuchcode zu Gesicht bekommen. Wer also nicht in der Lage ist auch sub-optimalen Code lesen zu können, der taugt als Programmierer einfach nichts (keine Sorge gibt genug Sparten wo es auch ohne geht: der Java, PHP und VB Markt ist groß).
Wichtig ist doch nur, dass eine Funktion keine zwei-stelligen Verschachtelungstiefen enthält. Selbst wenn eine Funktion mal 200 Zeilen lang ist so heißt das noch lange nicht, dass man nicht ohne größere Probleme nachvollziehen kann was sie tut.Und wer nicht in der Lage ist, den suboptimalen Code in etwas besseren Code zu ändern, wenn er eh grade dran sitzt, der taugt genausowenig wie derjenige, der den suboptimalen Code stehengelassen hat als er ihn fabriziert hat. Dazu zählen auch 200-Zeilen-Funktionen. "Ich kann Spaghetticode lesen, also darf ich auch Spaghetticode schreiben" ist genausowenig ein Argument wie "Ich hab zwar scheiß geschrieben, aber der Code war schon so scheiße, da macht das auch nichts mehr".
sprich fehlerüberprüfung und Aufrufen anderer Funktionen) und ich will oder kann sie nicht sofort zerkleinern, da noch gar nicht absehbar ist ob sie so bleibt. Und manchmal vergisst man es dann auch ganz einfach ...
Streich das "ich kann sie nicht zerkleinern" - du kannst schon, nur willst du nicht. Und wenn du wirklich ein echter Programmierer wärst, dann wüsstest du auch dass man nicht manchmal sondern quasi immer vergisst, es hinterher richtig zu machen, und dass es sehr wohl absehbar ist, dass die Funktion mit ziemlicher Sicherheit so bleibt wie sie gerade ist. Jedes Provisorium ist dauerhaft in dieser Branche.
-
Echter Progger schrieb:
Ich habe festgestellt, dass echte Programmierer mit Sprüngen in Assembler-Code klar kommen. Das heißt ohne, dass sie Ausschläge, Haarausfall, oder Sonstiges bekommen. Dementsprechend kommen Programmierer auch mit gotos in Hochsprachen klar...
Manche Programmierer versuchen, im Assembler-Code auch so wenig wie möglich Sprünge einzubauen. Weil beim Ausführen eines Sprungbefehls in der CPU die "Pipeline" (was immer man unter der Pipeline versteht) "entleert" und wieder "befüllt" werden muss, was vertane CPU Takte bedeutet.
-
das gilt aber nur unter bestimmten und ungünstigen Umständen, zB wenn die branch-prediction eine bedingte Verzweigung falsch vorhersagt (=> pipeline- und cache-flush). Die branch-prediction units moderner RISC-CPUs können Sprünge schon ziemlich gut vorhersagen und damit pipeline hazards usw vermeiden.
Oder natürlich, wenn die CPU gar keine branch-prediction hat - dann wird die CPU aber eventuell (wahrscheinlich) auch keine pipeline haben, und dann ist es wieder mehr oder minder egal, ob ein Sprung stattfindet oder nicht.
-
@u-ser_l:
Diese ungünstigen Umstände hat man in einigen Fällen recht oft. Bedingte Sprünge die ohne Muster "mal so mal so" ausgehen sind die Hölle.
Da kann es sich auch auf einer CPU mit guter Branch-Prediction auszahlen, Code zu schreiben, der gewisse Branches einspart.
-
sicher, sparen kann man immer was. Aber ich denke, das Thema branch-prediction ist recht gut erforscht - 96-99% korrekte Vorhersagen keine Seltenheit.
Gegen Zufallsbedingtes Verzweigungsverhalten ist natürlich schwierig anzukommen, aber die meisten bedingten Sprünge weisen offensichtlich Verhaltensmuster auf, die für gute Vorhersagen nutzbar sind.
-
96-99% korrekte Vorhersagen keine Seltenheit.
In einem for-loop mit nur 100 Iterationen rate ich einfach immer den Neueintritt in den Loop. Beim letzten Durchgang rate ich einmal falsch. Daraus folgt eine Vorhersagegenauigkeit von 99%, also kein grosses Kunststueck. Was will ich damit sagen? Zeige mir die Tests!
-
u-ser_l schrieb:
sicher, sparen kann man immer was. Aber ich denke, das Thema branch-prediction ist recht gut erforscht - 96-99% korrekte Vorhersagen keine Seltenheit.
Kindchen, das ist in Schleifen kein Wunder. Freu Dich über 99% bei Bubble-Sort und die anderen sind halt traurig über 50% bei Merge-Sort.
-
goto hell;
*scnr*
-
ach ja, in jedem thread über 'goto' darf das hier nicht fehlen: http://pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf
^^witzig, das pdf sieht so aus, als hätte jemand das papier 10 mal durch 'nen kopierer gezogen und dann eingescannt. trotzdem kann man textstellen auswählen und rauskopieren.
-
knivil schrieb:
Was will ich damit sagen? Zeige mir die Tests!
such sie dir doch selber heraus. Es gibt jedenfalls erheblich raffiniertere Strategien als nur zu sagen "Rücksprünge gehören meistens zu Schleifen, also unterstellen wir, daß Rücksprünge stets auch wirklich ausgeführt werden."
-
-
u-ser_l schrieb:
knivil schrieb:
Was will ich damit sagen? Zeige mir die Tests!
such sie dir doch selber heraus. Es gibt jedenfalls erheblich raffiniertere Strategien als nur zu sagen "Rücksprünge gehören meistens zu Schleifen, also unterstellen wir, daß Rücksprünge stets auch wirklich ausgeführt werden."
Und diese Strategien können auch einen Quicksort vorhersagen?
Clamping von beliebigen Eingabedaten?
Das Hinabsteigen in einen Baum zum Huffman Dekodieren?
-
hustbaer schrieb:
Und diese Strategien können auch einen Quicksort vorhersagen?
Clamping von beliebigen Eingabedaten?Na klar! Und auch noch das Wetter von übermorgen.
In der Praxis ist das Verhalten bedingter Sprünge nicht so zufällig, und dies kann man ausnutzen; durch Beobachtung der Vergangenheit und Vorhersage, durch Statistik, durch Heuristik, durch Analyse von Kombinationen verschiedener Sprungstellen im Code usw ...
Daß man selbst mit der besten Taktik keine zufälligen Sprünge oder Verhalten auf unstrukturierte Eingabedaten vorhersagen kann, ist so banal, daß sich eine Antwort darauf verbietet.
In der Praxis zählt halt meistens der average case und nicht der worst case.
-
u-ser_l schrieb:
In der Praxis ist das Verhalten bedingter Sprünge nicht so zufällig, und dies kann man ausnutzen; durch Beobachtung der Vergangenheit und Vorhersage, durch Statistik, durch Heuristik, durch Analyse von Kombinationen verschiedener Sprungstellen im Code usw ...
naja, ich weiss ja nicht ob viele prozessoren so ausgefeilte techniken benutzen. die meisten werden wohl nur statische 'branch prediction' machen. witzigerweise (habs erst letztens irgendwo gelesen) hat multitasking auf single-core CPUs (also wo durch'n timer-interrupt alle paar ms ein taskwechsel stattfindet) kaum 'nen negativen einfluss auf die sprungvorhersage.
btw, auf 'nem ARM7 z.b. verbrät eine 'mis-prediction' bloss 3 taktzyklen, bei 'ner x86-krücke sind's (glaub ich) ganze 20 oder so.
-
@;fricky:
Dass die Effizienz der Sprungvorhersage nicht grossartig durch preemptives Multitasking leidet, liegt einfach daran, dass eine Zeitscheibe für die CPU eine kleine Ewigkeit ist.Die Zeiten wo eine verpatzte Branch-Prediction 20 Zyklen gekostet hat sind AFAIK vorbei. Ich glaube die ganze Pipeline von einem Core 2/i7 ist keine 20 Zyklen mehr lang.
Und zumindest Pentium 3, 4, Core 2, i7, Athlon etc. machen alle ziemlich schlaue Branch-Prediction, mit Muster-Erkennung und allem Pipapo.
---------------------
@u-ser_l:
Touché
-
hustbaer schrieb:
Diese ungünstigen Umstände hat man in einigen Fällen recht oft.
in einigen Fällen recht oft - was jetzt: "in einigen Fällen" oder "recht oft" ?
wie oft ist recht oft nach deiner subjektiven Meinung ?
Ich hoffe, wir wissen beide, daß der Fall vollkommen zufälliger branches in der Praxis selten vorkommt und deshalb moderne branch-predictions einen guten Job machen.