Non c'è molto da lavorare qui. Non stai divulgando f
e inoltre non stai chiedendo di parallelizzare gli interni di f
, solo i cicli doppiamente nidificati che invocano f
. Mentre esiste una relazione tra f (, n) ed f (, n-1), non vedo come trarne vantaggio a causa del componente randomizzante non rivelato.
(presumo che questo sia il tuo intento, ma per essere chiari, potrebbe esserci una soluzione migliore se potessimo capire il componente randomizzante, dal momento che ripeterlo ripetutamente sembra che tutto il lavoro sia davvero, e fare qualcosa di diverso potrebbe invece essere più efficace.)
Quindi, l'unica cosa che puoi fare è tagliare i dati per mantenere occupati tutti i core.
Esistono tre metodi con cui è possibile suddividere i dati: subranges di A, subranges di B e subranges di N, l'ultimo dei quali hai già fatto con la tua risposta.
Inoltre non hai divulgato la struttura di A o B, tranne che sono ovviamente raccolte, o almeno generatori.
Se si tratta di una raccolta manifestata dalla memoria (array, liste, ecc.), quindi se sono di dimensioni significative, quindi iterando su A e B da ciascun core è possibile eseguire il thrash della cache, a meno che in qualche modo i nuclei cooperino e capiti di funziona allo stesso intervallo di A & B allo stesso tempo, quindi otterranno effettivamente una spinta l'uno dall'altro!
Ma se capita di essere significativamente fuori sincrono tra loro (diciamo perché la logica condizionale in f
non sta valutando lo stesso), quindi si combatteranno a vicenda per la cache.
(Un'analogia sta facendo la copia di cartelle / directory di grandi dimensioni sul tuo disco rigido. Se avvii un'altra copia di diverse cartelle contemporaneamente, entrambe le copie rallenteranno probabilmente la ricerca per indicizzazione e, insieme, eseguiranno 10 volte in serie somma delle copie.)
Quindi, per mitigare il combattimento della cache, se questo è davvero un problema, puoi scegliere di limitare le singole cpu a una porzione della matrice A x B che si adatta alla cache e avere tutto il lavoro della cpu su quell'insieme limitato che si adatta alla cache fino a quando tutte le CPU sono terminate, quindi passa ad un altro sottoinsieme della matrice A x B. Se una CPU termina per prima, probabilmente non gli darei nemmeno un nuovo lavoro, presumendo che (a) questo thrashing della cache sia un vero problema per il tuo dominio, e (b) che tutte le CPU finiranno con il loro sottoinsieme A x B in più- o meno allo stesso tempo. Supponendo che probabilmente saremmo più bravi a eseguire tutti i cpus fino al completamento sul sottoinsieme prima di iniziare il prossimo sottogruppo.
Ovviamente, anche tu vuoi generare il maggior numero di thread possibile perché rappresenta anche overhead, il che è un vantaggio dell'affinamento nella tua risposta. Ma è possibile che i thread non sincronizzati riducano la cache in modo sufficientemente risoluto che varrebbe la pena di generare thread aggiuntivi.
D'altra parte, generando esattamente tanti thread come core, ma con un algoritmo che funziona in modo incrementale su sottogruppi matriciali di A x B, quindi aspetta che tutti gli altri core confermino il completamento prima di iniziare il prossimo subrange può fornire il meglio di tutte le soluzioni. Ogni thread annuncia il completamento del sottoinsieme e quindi si sospende in attesa di notifica che tutti i thread sono stati completati.
Quindi ogni core passerebbe attraverso lo stesso sottoinsieme A x B usando la tua nozione di iterazioni n / C, quindi tutti i core passerebbero al successivo sottoinsieme A x B.
In effetti, anche l'utilizzo di subranges per A x B su un singolo algoritmo threadded può migliorare le prestazioni rispetto a tutte le A x B numerose volte, il che è del tutto possibile se la memoria toccata nella matrice A x B non si adatta nella cache. Ogni corsa attraverso A x B deve riportare l'intera struttura di entrambi i dati nella cache (e forse anche ancora e ancora solo per una iterazione attraverso A x B), mentre un singolo thread in esecuzione su un sottoinsieme gestibile potrebbe portare ognuno di questi sottoinsiemi nella cache solo una volta per le iterazioni, quindi potresti iniziare con una singola versione a thread di subset di matrice, quindi aggiungere la sincronizzazione parallela.