Parallelisierung mit OpenMP / Dynamisch threads erzeugen
-
Hab jetzt mit TDragon ausprobiert, kann openmp, aber das erzeugte programm stürzt ab wenn er in den openmp teil kommt (mit den sections).
Ohne OpenMP braucht das programm 2:50, also ziemlich genau so lange wie auch die 32-bit MSVC8 version.Ich hab nochmal die OpenMP versionen getestet und kann meine ergebnisse von oben nicht mehr reproduzieren, die Werte schwanken jetzt zwischen 1:03 und 4:05 (das war genau die gleiche kommandozeile direkt hintereinander). Ich weiß nicht woran das liegt, beide male war prozessorauslastung von dem programm durchgängig >90%, meistens schwankte es zwischen 97 und 99.
ich hab jetzt mal das mit tasks gemacht. ich habe aber noch nicht genau verstanden wie die funktionieren, die ergebnisse sind folgende:
wenn ich es so mache wie hier in beispiel 1, kriege ich zwei threads aber nach ca. 10 sekunden ist nur noch einer ausgelastet und insgesamt brauch das programm knapp 6 minuten. Es ist auch nicht anders wenn ich die anzahl der threads erhöhe, nach 10 sekunden ist max. cpu auslastung <50%.
wenn ich es so mache wie in 2a (zwei task-constructs) stürzt es wieder ab, bei 2b (ein task-construct) bekomme ich nur ein thread.
Gibt es irgendwo eine schöne erklärung wie tasks funktionieren und wie sie sich von parallel sections unterscheiden? Die Seiten zu OpenMP die ich gefunden habe behandeln Tasks nicht.Vielen Dank
Stefan
-
Kann es sein dass du furchtbar viele zeitintensive Synchronisierungen hast? Poste bitte nochmal den sequentiellen Code, wenn moeglich so dass man auch sieht wie rechneA und rechneB auch wieder threads starten.
Tutorials zu tasks fallen mir grad keine ein, aber wenn du "OpenMP 3.0" googelst, findest du 'nen Haufen. Das Prinzip ist aber eigentlich nicht schwer zu verstehen: Du startest eine parallele Region, laesst da dann einen einzelnen Thread durchgehen, der jedesmal, wenn er auf einen "task" stoesst, diesen in eine Queue steckt, von wo ihn die anderen Threads rausnehmen und bearbeiten.
Spontan wuerd ich's so machen (ohne wirklich zu verstehen was der Code macht etc.)
datentyp *liste, *var; #pragma omp parallel { #pragma omp single nowait { for(i=0;i<N;i++) { if(RechneWas(var,i)) { #pragma omp task firstprivate(var, i) RechneB(var,i); RechneA(var,i); } } #pragma omp critical if(IstBrauchbar(var)) { liste->next = var; var->prev = liste; liste = var; if(IstPerfekt(var)) { KillAllTherads(); } } } } Print(liste);
Was ich aber nicht versteh: warum parallelisierst du nicht einfach die for-Schleife?
EDIT: es kann natuerlich auch sein, dass die Parallelisierung einfach ein paar sehr super Optimierungen des Compilers kaputt macht. Was macht denn der sequentielle Code bei -O0 ?
-
Ich hoffe das ist jetzt nicht zu viel code:
void findfrequences(fcalc *v, globalvars *g) { fcalc *vb; fsol *s; int stop, lim; if(v->fn>=g->totalfreq) return; //#pragma omp critical (stop) // { stop = g->stop; // } if(stop) return; for(;v->i<g->fs_n;v->i++) { for(;v->j<g->fs[v->i].n;v->j++) { for(;v->q<g->fs[v->i].freqlist_n;v->q++) { if (checkspace(v,g->fs[v->i].freqlist[v->q],g->fs[v->i].fminim,g->fs[v->i].fmaxim)) continue; if (g->opt_2im3 && check2im3(v,g->fs[v->i].freqlist[v->q],g->opt_2im3)) continue; if (g->opt_2im5 && check2im5(v,g->fs[v->i].freqlist[v->q],g->opt_2im5)) continue; if (g->opt_3im3 && check3im3(v,g->fs[v->i].freqlist[v->q],g->opt_3im3)) continue; // freq works vb=copyfcalc(v,g); #pragma omp parallel sections { #pragma omp section { if(g->opt_2im3) add2im3(v, g->fs[v->i].freqlist[v->q], g->opt_2im3); if(g->opt_2im5) add2im5(v, g->fs[v->i].freqlist[v->q], g->opt_2im5); if(g->opt_3im3) add3im3(v, g->fs[v->i].freqlist[v->q], g->opt_3im3); addfreq(v,g->fs[v->i].freqlist[v->q],g->opt_s,g->fs[v->i].fminim,g->fs[v->i].fmaxim); v->q++; if(v->fn<g->totalfreq) { findfrequences(v,g); stop=2; } if(stop!=2) stop=1; } #pragma omp section { vb->q++; findfrequences(vb,g); } } if(stop==2) return; if(stop) break; } if(stop) break; } if(stop) break; v->j=0; v->q=0; } if((v->fn==0) || v->fn<g->opt_m) { free(v); return; } if(v->fn>=g->totalfreq) { #pragma omp critical (fullsol) { g->fullsol++; if(g->fullsol>=g->opt_e) { #pragma omp critical (stop) { g->stop = 1; } } } } s = sec_malloc(sizeof(fsol)); s->fn = v->fn; s->flist = sec_malloc(sizeof(int)*s->fn); memcpy(s->flist,v->flist,sizeof(int)*s->fn); s->next = NULL; free(v); #pragma omp critical (result) { if(!g->sol_start) { s->prev = NULL; g->sol_start = g->sol_end = s; } else { s->prev = g->sol_end; g->sol_end->next = s; g->sol_end = s; } } return; }
Das Problem ist, ich kann die for-Schleife nicht einfach parallelisieren, da die Rechnung des i+1-ten Durchlauf vom Ergebnis des i-ten Durchlaufs abhängig ist. Wenn i-te durchlauf
Das zeitintensive ist hier zeile 23, denn v ist selbst in meinem kurzen benchmark bereits knapp 128KB groß.Synchronisierung habe ich fast keine, 99% der aufrufe dieser funktion werden in zeile 46 oder 58 enden. Ich habe das critical beim lesen der stop-variable mal auskommentiert, geht auch ohne problemlos. Ich mein, das ist ein int, da wird entweder der alte oder der neue wert stehen, es kann ja nicht zeitgleich ein thread auf eine int-variable zugreifen während ein anderer sie beschreibt, oder? beim array wäre das natürlich anders.
Das was du vorschlägst entspricht ja ungefär dem was bei sun unter 1 steht, nur das du nur ein task machst.
Ich hab den code mal so geändert:
void findfrequences(fcalc *v, globalvars *g) { fcalc *vb; fsol *s; int stop, lim; if(v->fn>=g->totalfreq) return; //#pragma omp critical (stop) // { stop = g->stop; // } if(stop) return; for(;v->i<g->fs_n;v->i++) { for(;v->j<g->fs[v->i].n;v->j++) { for(;v->q<g->fs[v->i].freqlist_n;v->q++) { if (checkspace(v,g->fs[v->i].freqlist[v->q],g->fs[v->i].fminim,g->fs[v->i].fmaxim)) continue; if (g->opt_2im3 && check2im3(v,g->fs[v->i].freqlist[v->q],g->opt_2im3)) continue; if (g->opt_2im5 && check2im5(v,g->fs[v->i].freqlist[v->q],g->opt_2im5)) continue; if (g->opt_3im3 && check3im3(v,g->fs[v->i].freqlist[v->q],g->opt_3im3)) continue; // freq works if(v->fn+1>=g->totalfreq) { vb=v; stop=1; } else { vb=copyfcalc(v); stop=0; } if(g->opt_2im3) add2im3(vb, g->fs[vb->i].freqlist[vb->q], g->opt_2im3); if(g->opt_2im5) add2im5(vb, g->fs[vb->i].freqlist[vb->q], g->opt_2im5); if(g->opt_3im3) add3im3(vb, g->fs[vb->i].freqlist[vb->q], g->opt_3im3); addfreq(vb,g->fs[vb->i].freqlist[vb->q],g->opt_s,g->fs[vb->i].fminim,g->fs[vb->i].fmaxim); vb->q++; if(stop) break; #pragma omp task findfrequences(vb,g); } } v->j=0; v->q=0; } if((v->fn==0) || v->fn<g->opt_m) { free(v); return; } if(v->fn>=g->totalfreq) { #pragma omp critical (fullsol) { g->fullsol++; if(g->fullsol>=g->opt_e) { #pragma omp critical (stop) { g->stop = 1; } } } } s = sec_malloc(sizeof(fsol)); s->fn = v->fn; s->flist = sec_malloc(sizeof(int)*s->fn); memcpy(s->flist,v->flist,sizeof(int)*s->fn); s->next = NULL; free(v); #pragma omp critical (result) { if(!g->sol_start) { s->prev = NULL; g->sol_start = g->sol_end = s; } else { s->prev = g->sol_end; g->sol_end->next = s; g->sol_end = s; } } return; }
Und beim ersten Aufruf der Funktion:
#pragma omp parallel { #pragma omp single nowait { findfrequences(v,&g); } }
Stürzt bloß leider ab. auch wenn ich parallel/single innerhalb der funktion mache um die äußerste schleife passiert genau das gleiche. ich frag mich ob ich da was falsch mache oder openmp mit mingw nicht so toll funktioniert. schließlich stürzt auch die version mit sections, die mit msvc nur langsam ist, sofort ab.
ich werde sonst mal mein linux bemühen den code zu kompilieren, vielleicht geht es da ja.
wenn ich openmp deaktiviere ist mit diesem code die x64-version 2 sekunden und win32 version sogar 40 sek. schneller (liegt vielleicht an der calling convention).Vielen Dank
Stefan
-
Ich habs jetzt unter Linux probiert und mein verdacht hat sich bestätigt, mingw und openmp geht nicht. Unter Linux kompiliert und lief sofort.
Ohne OpenMP: 53 sek. (sehr gut reproduzierbar)
Mit OpenMP und Tasks so wie im vorherigen Post umgesetzt: 40-50 sek. (schwankend)
Alles andere ist bedeutend langsamer (sections)
Mir ist aufgefallen das die Gesamtprozessorauslastung ohne OpenMP 50%+x (x = andere Programme) beträgt, mit OpenMP aber schwankt und nicht durchgängig bei 100% ist. Ich weiß aber nicht was da bremst.
Mein Versuch memcpy durch eine selbstgeschriebene sse-asm-version zu ersetzen: geht nicht, das beste was ich geschafft hab war 1 sekunde langsamer als mit standardfunktion.Eine Idee die ich noch hätte wäre - wenn das geht - nicht immer tasks zu erstellen, sondern ein globalen thread-counter zu machen und nur wenn ein task beendet wurde wird an der nächsten Gabelung ein Task gestartet.
Sonst noch irgendwelche Ideen? ich mein theoretisch müsste sich das Problem doch super parallelisieren lassen, da sollten doch 30 sekunden im Bereich des Machbaren liegen mit 2 threads wenn ein thread 53 sekunden braucht.
Vielen Dank
Stefan
-
Hmm... die erste deiner 2 geposteten Versionen ist "falsch", da du bei jedem Aufruf von findfrequencies einen neuen "omp parallel"-Abschnitt hast. Und jeder "omp parallel"-Abschnitt startet dir X neue Threads.
Bei der 2. Version (der mit den Tasks) seh ich jetzt breim grob drueber schauen keinen offensichtlichen Fehler (abgesehen davon dass du 2 critical-sections ineinander verschachtelst, was eigentlich sinnlos ist. Ich schau mir's morgen nochmal genauer an.
Stefan_O schrieb:
Eine Idee die ich noch hätte wäre - wenn das geht - nicht immer tasks zu erstellen, sondern ein globalen thread-counter zu machen und nur wenn ein task beendet wurde wird an der nächsten Gabelung ein Task gestartet.
Wozu? die Threads werden ja alle beim "omp parallel" gestartet und wartn dann alle auf neue Tasks (bis auf den einen Thread, der in die "single"-Section rein kommt). D. h. OpenMP macht das eh automatisch.
-
Blue-Tiger schrieb:
Hmm... die erste deiner 2 geposteten Versionen ist "falsch", da du bei jedem Aufruf von findfrequencies einen neuen "omp parallel"-Abschnitt hast. Und jeder "omp parallel"-Abschnitt startet dir X neue Threads.
Mehr Threads starten als festgelegt tut er nicht, aber es ist wie gesagt extrem langsam.
Blue-Tiger schrieb:
Bei der 2. Version (der mit den Tasks) seh ich jetzt breim grob drueber schauen keinen offensichtlichen Fehler (abgesehen davon dass du 2 critical-sections ineinander verschachtelst, was eigentlich sinnlos ist. Ich schau mir's morgen nochmal genauer an.
Wiso? Das sind doch zwei unterschiedliche critical sections, auch wenn die innere keinen Sinn mehr hat wenn sie oben auskommentiert ist.
Blue-Tiger schrieb:
Wozu? die Threads werden ja alle beim "omp parallel" gestartet und wartn dann alle auf neue Tasks (bis auf den einen Thread, der in die "single"-Section rein kommt). D. h. OpenMP macht das eh automatisch.
Natürlich um das programm schneller zu machen
So scheint es ja noch nicht optimal zu funktionieren. Desweiteren fehlt mir noch eine Lösung für windows. mingw will schon bei win32 OpenMP nicht (anscheinend funktionieren nur ältere versionen, da gibts aber noch kein OpenMP 3.0), und da x64 deutlich schneller brauch ich mingw-w64 mit openmp, dazu finde ich gar nichts. Das heißt ich brauch wohl eine Lösung die ohne tasks auskommt.Vielen Dank
Stefan
-
Stefan_O schrieb:
Blue-Tiger schrieb:
Hmm... die erste deiner 2 geposteten Versionen ist "falsch", da du bei jedem Aufruf von findfrequencies einen neuen "omp parallel"-Abschnitt hast. Und jeder "omp parallel"-Abschnitt startet dir X neue Threads.
Mehr Threads starten als festgelegt tut er nicht, aber es ist wie gesagt extrem langsam.
Na ja, es ist auch sehr,sehr langsam, da du durch die Verschachtelten "parallel"-sections den gleichen Code viel oefters ausfuehrst als du eigentlich willst: http://software.intel.com/en-us/articles/32-openmp-traps-for-c-developers/#IDANOQDC (allgemein sehr empfehlenswert, der Artikel).
Blue-Tiger schrieb:
Wozu? die Threads werden ja alle beim "omp parallel" gestartet und wartn dann alle auf neue Tasks (bis auf den einen Thread, der in die "single"-Section rein kommt). D. h. OpenMP macht das eh automatisch.
Natürlich um das programm schneller zu machen
So scheint es ja noch nicht optimal zu funktionieren. Desweiteren fehlt mir noch eine Lösung für windows. mingw will schon bei win32 OpenMP nicht (anscheinend funktionieren nur ältere versionen, da gibts aber noch kein OpenMP 3.0), und da x64 deutlich schneller brauch ich mingw-w64 mit openmp, dazu finde ich gar nichts. Das heißt ich brauch wohl eine Lösung die ohne tasks auskommt.Hmm... mit MinGW/Windows kann ich dir leider nicht helfen. Ich geh davon aus dass du beim TDragon-Release alles richtig gemacht hast (du musst sowohl -gomp als auch -lpthreads als Optionen mitgben). Find es allerdings sehr komisch dass das nicht geht (zumindest mit TDragon-gcc 4.2 (oder war es 4.3?) funktionierte OpenMP noch einwandfrei (Leider gibts erst ab 4.4 support fuer omp tasks). Funktionieren denn andere Minimal-OpenMP-Beispiele mit dem TDragon-gcc?
Auf sections wuerd ich nicht zurueckgreifen, die sind einfach nicht fuer das gemacht was du vorhast. Notfalls muesstest du wohl von Hand Threads programmieren; ist auch nicht sooooo schwer (und PThreads gibts sowohl fuer Linux als auch fuer Windows, und ich vermute auch fuer Macs).
Eine (von mir nicht getestete) Moeglichkeit waer noch, von Linux aus fuer Windows crosscompilen, geht bei neueren Distris relativ einfach (die haben oft irgendwelche mingw-packages), aber k.A. wie's da mit GOMP (GNU OpenMP)-support ausschaut. Noch extremer: du koenntest dir den MinGW selbst bauen (aber eigentlich sollte das TDragon-Release schon funktionieren).
-
Na ja, es ist auch sehr,sehr langsam, da du durch die Verschachtelten "parallel"-sections den gleichen Code viel oefters ausfuehrst als du eigentlich willst: http://software.intel.com/en-us/articles/32-openmp-traps-for-c-developers/#IDANOQDC (allgemein sehr empfehlenswert, der Artikel).
Ist es den möglich innerhalb einer section eine weitere zu haben? für den Fall könnte man das omp parallel ja rausziehen aus der funktion und innerhalb die sections haben.
Hmm... mit MinGW/Windows kann ich dir leider nicht helfen. Ich geh davon aus dass du beim TDragon-Release alles richtig gemacht hast (du musst sowohl -gomp als auch -lpthreads als Optionen mitgben). Find es allerdings sehr komisch dass das nicht geht (zumindest mit TDragon-gcc 4.2 (oder war es 4.3?) funktionierte OpenMP noch einwandfrei (Leider gibts erst ab 4.4 support fuer omp tasks). Funktionieren denn andere Minimal-OpenMP-Beispiele mit dem TDragon-gcc?
Nein, mit dem TDragon-gcc funktioniert nichts mit OpenMP. Ich habe gelesen das OpenMP nur bei älteren builds funktioniert hat. Und selbst wenn es gehen würde, ist es nicht 64-bit. Das problem scheint zu sein das libgomp zur Zeit nicht mit win32 threads funktioniert und libgomp->pthreads->win32 threads instabil ist. Das heißt cross-compiling hilft auch nicht.
Der Intel-Compiler kann OpenMP 3.0, ist aber für Windows nicht kostenlos, nur 30-tage demo. Und man muss ihn überlisten damit die erzeugten programme auch mit AMD prozessoren schnell sind.Auf sections wuerd ich nicht zurueckgreifen, die sind einfach nicht fuer das gemacht was du vorhast. Notfalls muesstest du wohl von Hand Threads programmieren; ist auch nicht sooooo schwer (und PThreads gibts sowohl fuer Linux als auch fuer Windows, und ich vermute auch fuer Macs).
Muss man da was komplett anderes machen oder kann man das einfach mit #ifdef und 3 zeilen code für jedes OS lösen? Geht das mit den critical sections so einfach oder muss ich da was kompliziertes bauen?
Auch wenns off-topic ist, aber ich bin zufällig als ich nach einem schnellen memcpy gesucht habe, darauf gestoßen, dass
if (a<0 || a>=b) continue;
für schnellere ausführung durch
if ((unsigned int)a >= (unsigned int)b) continue;
ersetzt werden sollte. da das ein paar mal vorkommt in den Funktionen die innerhalb der schleife aufgerufen werden, dacht ich mir, vielleicht bringt es ja einen messbaren vorteil ansonsten schadet es nicht:
Von 59 sekunden runter auf 39! ich dachte ich bin blöd, habe aber die zeilen wieder zurückgeändert, nochmal kompiliert, aber es liegt wirklich daran. wenn man mal nachzählt taucht das auch in jeder check-funktionen je nachdem wie viel er schon gerechnet hat zwischen 4 und 45 mal auf in meinem test.Vielen Dank
Stefan
-
Stefan_O schrieb:
Na ja, es ist auch sehr,sehr langsam, da du durch die Verschachtelten "parallel"-sections den gleichen Code viel oefters ausfuehrst als du eigentlich willst: http://software.intel.com/en-us/articles/32-openmp-traps-for-c-developers/#IDANOQDC (allgemein sehr empfehlenswert, der Artikel).
Ist es den möglich innerhalb einer section eine weitere zu haben? für den Fall könnte man das omp parallel ja rausziehen aus der funktion und innerhalb die sections haben.
Da bin ich ueberfragt, mit section kenn ich mich nicht naeher aus
Hmm... mit MinGW/Windows kann ich dir leider nicht helfen. Ich geh davon aus dass du beim TDragon-Release alles richtig gemacht hast (du musst sowohl -gomp als auch -lpthreads als Optionen mitgben). Find es allerdings sehr komisch dass das nicht geht (zumindest mit TDragon-gcc 4.2 (oder war es 4.3?) funktionierte OpenMP noch einwandfrei (Leider gibts erst ab 4.4 support fuer omp tasks). Funktionieren denn andere Minimal-OpenMP-Beispiele mit dem TDragon-gcc?
Nein, mit dem TDragon-gcc funktioniert nichts mit OpenMP. Ich habe gelesen das OpenMP nur bei älteren builds funktioniert hat. Und selbst wenn es gehen würde, ist es nicht 64-bit. Das problem scheint zu sein das libgomp zur Zeit nicht mit win32 threads funktioniert und libgomp->pthreads->win32 threads instabil ist. Das heißt cross-compiling hilft auch nicht.
Interessant, thx fuer die Info
(Hast du zufaellig Quellen oder Bugreports, damit man den Fortschritt mitverfolgen kann?
)
Auf sections wuerd ich nicht zurueckgreifen, die sind einfach nicht fuer das gemacht was du vorhast. Notfalls muesstest du wohl von Hand Threads programmieren; ist auch nicht sooooo schwer (und PThreads gibts sowohl fuer Linux als auch fuer Windows, und ich vermute auch fuer Macs).
Muss man da was komplett anderes machen oder kann man das einfach mit #ifdef und 3 zeilen code für jedes OS lösen? Geht das mit den critical sections so einfach oder muss ich da was kompliziertes bauen?
Na ja, normale multithreading-libraries unterscheiden sich grundlegend von OpenMP. Du musst wesentlich mehr selbst Hand anlegen, d.h. Threads von Hand erstellen, Daten auf Threads verteilen, etc. Rechne mal 1-2 Tage Lernaufwand, wenn du mit der grundlegenden Problematik vertraut bist.
Es gibt verschiedene Libs. Wenn du mit C arbeitest bietet sich pthreads an.
pthreads gibts dann sowohl fuer Windows, MacOS als auch fuer Linux. Du benoetigst daher also nichtmal ein #ifdef
(Pthreads fuer 64bit-Windows muss man sich anscheinend selbst kompilieren, aber es funktioniert laut http://old.nabble.com/pthreads-on-64bit-Windows-td27329918.html ).Auch wenns off-topic ist, aber ich bin zufällig als ich nach einem schnellen memcpy gesucht habe, darauf gestoßen, dass
if (a<0 || a>=b) continue;
für schnellere ausführung durch
if ((unsigned int)a >= (unsigned int)b) continue;
ersetzt werden sollte. da das ein paar mal vorkommt in den Funktionen die innerhalb der schleife aufgerufen werden, dacht ich mir, vielleicht bringt es ja einen messbaren vorteil ansonsten schadet es nicht:
Von 59 sekunden runter auf 39! ich dachte ich bin blöd, habe aber die zeilen wieder zurückgeändert, nochmal kompiliert, aber es liegt wirklich daran. wenn man mal nachzählt taucht das auch in jeder check-funktionen je nachdem wie viel er schon gerechnet hat zwischen 4 und 45 mal auf in meinem test.Na klar, ein unsigned kann auch nicht <0 sein, deswegen muss der Compiler das auch nicht ueberpruefen. Er laesst die Ueberpruefung also einfach weg.
Wenn a trotzdem <0 sein kann, wird dein Code allerdings nicht mehr richtig funktionieren. Wenn a nie kleiner als 0 sein kann, haettest du's sowieso am besten von vorn herein als unsigned deklariert. Das macht den Code eindeutiger (weil dann jeder sieht dass a>=0) und hilft wie du selbst gemerkt hast, dem Compiler beim optimieren.
Grundsaetzlich kannst du ausserdem davon ausgehen dass memcpy optimal implementiert ist. Wuerd mich wundern wenn du da was schnelleres hinkriegst als die Leute, die den Compiler/die Standardbibliothek geschrieben haben; ausser du hast sehr spezielle Anforderungen.
-
Blue-Tiger schrieb:
Na klar, ein unsigned kann auch nicht <0 sein, deswegen muss der Compiler das auch nicht ueberpruefen. Er laesst die Ueberpruefung also einfach weg.
Wenn a trotzdem <0 sein kann, wird dein Code allerdings nicht mehr richtig funktionieren.
Der Trick ist, dass die Zahlen kleiner als 0 als unsigned interpretiert groß sind und vorallem größer als der größte signed integer. Daher ist das eine Abkürzung. Wobei ich nicht weiß, was der Standard dazu sagt. Ob das im Grunde UB ist :).
Wundert mich eigentlich, dass der GCC das nicht selbst macht, weil der ähnliche Fälle optimieren kann.
-
rüdiger schrieb:
Blue-Tiger schrieb:
Na klar, ein unsigned kann auch nicht <0 sein, deswegen muss der Compiler das auch nicht ueberpruefen. Er laesst die Ueberpruefung also einfach weg.
Wenn a trotzdem <0 sein kann, wird dein Code allerdings nicht mehr richtig funktionieren.
Der Trick ist, dass die Zahlen kleiner als 0 als unsigned interpretiert groß sind und vorallem größer als der größte signed integer. Daher ist das eine Abkürzung. Wobei ich nicht weiß, was der Standard dazu sagt. Ob das im Grunde UB ist :).
Aber nur wenn b immer >=0 ist, oder missverstehe ich was?