perché GCC __builtin_prefetch non migliora le prestazioni?

Sto scrivendo un programma per analizzare un grafico dei social network. Significa che il programma ha bisogno di molti accessi casuali alla memoria. Mi sembra che il prefetch dovrebbe aiutare. Ecco una piccola parte del codice di lettura dei valori dai vicini di un vertice.

for (size_t i = 0; i < v.get_num_edges(); i++) { unsigned int id = v.neighbors[i]; res += neigh_vals[id]; } 

Trasformo il codice sopra a quello come sotto e prefetch i valori dei vicini di un vertice.

 int *neigh_vals = new int[num_vertices]; for (size_t i = 0; i < v.get_num_edges(); i += 128) { size_t this_end = std::min(v.get_num_edges(), i + 128); for (size_t j = i; j < this_end; j++) { unsigned int id = v.neighbors[j]; __builtin_prefetch(&neigh_vals[id], 0, 2); } for (size_t j = i; j < this_end; j++) { unsigned int id = v.neighbors[j]; res += neigh_vals[id]; } } 

In questo codice C ++, non ho ignorato alcun operatore.

Sfortunatamente, il codice non migliora veramente le prestazioni. Mi chiedo perché. Apparentemente, il prefetch dell’hardware non funziona in questo caso perché l’hardware non può prevedere la posizione della memoria.

Mi chiedo se sia causato dall’ottimizzazione GCC. Quando compilo il codice, abilito -O3. Spero davvero che prefetch possa migliorare ulteriormente le prestazioni anche quando -O3 è abilitato. L’ottimizzazione dell’O3 fonde i due anelli in questo caso? Can -O3 abilita il prefetch in questo caso per impostazione predefinita?

Io uso gcc versione 4.6.3 e il programma gira su Intel Xeon E5-4620.

Grazie, Da

Sì, alcune versioni recenti di GCC (ad esempio 4.9 di marzo 2015) sono in grado di emettere alcune istruzioni PREFETCH durante l’ottimizzazione con -O3 (anche senza esplicito __builtin_prefetch )

Non sappiamo cosa sta facendo get_neighbor e quali sono i tipi di v e neigh_val .

E il prefetching non è sempre redditizio. L’aggiunta di __builtin_prefetch esplicita può rallentare il tuo codice. Devi misurare.

Come commentava Ninja in pensione , il prefetching in un ciclo e i dati sperati sarebbero stati memorizzati nella cache nel seguente ciclo (più in basso nel codice sorgente).

Potresti forse provare invece

 for (size_t i = 0; i < v.get_num_edges(); i++) { fg::vertex_id_t id = v.get_neighbor(i); __builtin_prefetch (neigh_val[v.get_neighbor(i+4)]); res += neigh_vals[id]; } 

Potresti sostituire empiricamente il 4 con qualunque costante appropriata sia la migliore.

Ma suppongo che il __builtin_prefetch sopra sia inutile (dal momento che il compilatore è probabilmente in grado di aggiungerlo da solo) e potrebbe danneggiare (o addirittura mandare in crash il programma, quando il suo argomento fornisce un comportamento indefinito, ad esempio se v.get_neighbor(i+4) non è definito, tuttavia il prefetching di un indirizzo al di fuori del tuo spazio indirizzo non danneggerà, ma potrebbe rallentare il tuo programma). Si prega di riferimento.

Vedi questa risposta a una domanda correlata.

Si noti che in C ++ tutti [] , get_neighbor potrebbe essere sovraccaricato e diventa un'operazione molto complessa, quindi non possiamo indovinarlo!

E ci sono casi in cui l'hardware limita le prestazioni, qualunque sia il __builtin_prefetch che aggiungi (e aggiungendole potrebbe danneggiare le prestazioni)

A proposito, potresti passare -O3 -mtune=native -fdump-tree-ssa -S -fverbose-asm per capire meglio cosa sta facendo il compilatore (e guardare all'interno dei file di dump generati e dei file assembler); inoltre, succede che -O3 produce un codice leggermente più lento di quello che dà -O2 .

Potresti considerare il multithreading esplicito, OpenMP , OpenCL se hai tempo da sprecare per l'ottimizzazione. Ricorda che l'ottimizzazione prematura è malvagia . Hai fatto un punto di riferimento, hai profilato la tua intera domanda?