Calcolo di area di poligoni originariamente in Montecarlo, riscritto in maniera esatta, credo sia passato a un decimo del tempo.
Algoritmo di espansione di aree su immagine, scritto in maniera cretina con astrazioni e allocazioni inutili ci metteva tipo un secondo e mezzo, riscritto a modino 200 ms o qualcosa del genere (con tra l'altro una feature di conservazione delle aree piccole ma significative che mancava all'altro).
Parsing di un formato vagamente tipo ISO in C++; lettura malamente bufferizzata di una riga alla volta, ogni volta allocata dinamicamente ex-novo (buffer fuori dal ciclo), con una trim (due allocazioni inutili) e diverse substr solo per fare confronti di inizio stringa (peraltro in if in sequenza invece che in else if). Sistemato il buffering, spostata la stringa fuori dal loop (con clear ogni volta, ma senza effettivamente liberare la memoria), uccise tutte le substr, trim inplace, da una decina di allocazioni inutili nel loop a zero. È passato da circa 8 secondi (su un caso di test) a un centinaio di ms.
Dump di file testuale lento come lo schifo. Andando di profiler salta fuori che il collo di bottiglia è la fprintf, che perde una marea di tempo a fare delle getenv.
Long story short, fino ad una certa versione di MinGW non sapevano decidersi se la printf con il %f di default dovesse avere non so se 3 o 4 decimali, perché di default mi sembra glibc aveva 4 mentre il runtime Microsoft ne aveva 3 e quindi hanno fatto che si poteva andare in override con una variabile d'ambiente.
La cosa, già discutibile di per sé, è resa disastrosa dal fatto che questa variabile d'ambiente viene riletta ad ogni chiamata (cosa abbastanza senza senso, visto che di norma le variabili d'ambiente non cambiano dopo l'avvio del processo), e la getenv è una funzione mediamente lenta rispetto a printf "semplici", dato che tutte le volte ri-effettua una ricerca lineare dentro il blocco dell'environment del processo (senza mai trovare la stringa che cerca, quindi caso pessimo). Morale della cosa, le printf in media erano 8 volte più lente del dovuto.
Nei MinGW successivi avevano risolto la cosa cachando il risultato di questo lookup, ma cambiare il compilatore (e ricompilare una montagna di librerie tra cui Qt) due minuti prima di un rilascio era lungo e rischioso. Ergo: ho fatto una patch binaria alla libreria standard.
Cambiando tre bit nella libreria statica il tempo di dump è passato da un 30 secondi a 5.La funzione in causa è __mingw_pformat in libmingwex.a, in particolare a 0x1699:
La call in questione nella libreria ha come target l'offset 0, ma è un placeholder che viene riempito dal linker con l'indirizzo del trampolino per la getenv (motivo per cui non si può semplicemente asfaltare con dei nop, il linker li sovrascriverebbe).codice:1687: 31 c0 xor eax,eax 1689: 66 89 44 24 50 mov WORD PTR [esp+0x50],ax 168e: 8b 84 24 88 00 00 00 mov eax,DWORD PTR [esp+0x88] 1695: 89 44 24 58 mov DWORD PTR [esp+0x58],eax 1699: e8 00 00 00 00 call 169e <___mingw_pformat+0x8e> 169e: 85 c0 test eax,eax 16a0: 74 10 je 16b2 <___mingw_pformat+0xa2> 16a2: 0f be 10 movsx edx,BYTE PTR [eax] 16a5: b8 02 00 00 00 mov eax,0x2 16aa: 83 ea 30 sub edx,0x30 16ad: 83 fa 02 cmp edx,0x2 16b0: 76 0d jbe 16bf <___mingw_pformat+0xaf> 16b2: e8 00 00 00 00 call 16b7 <___mingw_pformat+0xa7> 16b7: 83 e0 01 and eax,0x1
La modifica effettuata è:
La call a 0x1699 diventa una mov (per cui in eax va a finire l'indirizzo del trampolino della getenv, sicuramente diverso da zero); la je a 0x16a0 invece diventa una jne. In questa maniera, attiviamo sempre il percorso di codice relativo al caso in cui getenv restituisce NULL (che è quello che di fatto verrebbe attivato sempre). L'unica differenza è che in questo caso eax resta diverso da zero, ma viene immediatamente sovrascritto dalla call a 0x16b2 (a cui punta la jne), quindi non è un problema.codice:1687: 31 c0 xor eax,eax 1689: 66 89 44 24 50 mov WORD PTR [esp+0x50],ax 168e: 8b 84 24 88 00 00 00 mov eax,DWORD PTR [esp+0x88] 1695: 89 44 24 58 mov DWORD PTR [esp+0x58],eax 1699: b8 00 00 00 00 mov eax,0x0 169e: 85 c0 test eax,eax 16a0: 75 10 jne 16b2 <___mingw_pformat+0xa2> 16a2: 0f be 10 movsx edx,BYTE PTR [eax] 16a5: b8 02 00 00 00 mov eax,0x2 16aa: 83 ea 30 sub edx,0x30 16ad: 83 fa 02 cmp edx,0x2 16b0: 76 0d jbe 16bf <___mingw_pformat+0xaf> 16b2: e8 00 00 00 00 call 16b7 <___mingw_pformat+0xa7> 16b7: 83 e0 01 and eax,0x1![]()




Rispondi quotando