Flerkjerneprosessor? Ja.
Skrive program for å kjøre parallelt? Ja.
Husket du å bruke en skalerbar minnetildeler? Nei? Les videre ...
I min erfaring er det å sørge for at "minnetildeling" for et program er klar for parallellitet, et ofte oversett element for å få et parallelt program til å fungere godt. Jeg kan vise deg en utrolig enkel måte å se om dette er et problem for et samlet program (C, C ++, Fortran, etc.) - samt hvordan du fikser det.
En kritisk del av ethvert parallelt program er skalerbar minnetildeling, som inkluderer bruk avnysamt eksplisitte samtaler tilmalloc, calloc eller realloc. Alternativene inkluderer TBBmalloc (Intel Threading Building Blocks), jemalloc og tcmalloc. TBBmalloc har en ny "proxy" -funksjon som gjør det enkelt å prøve på mindre enn 5 minutter på et kompilert program.
Ytelsesfordelene ved å bruke en skalerbar minnetildeler er betydelige. TBBmalloc var blant de første mye brukte skalerbare minnetildelere, ikke bare fordi det kom gratis med TBB for å understreke viktigheten av å inkludere hensyn til minnetildeling i ethvert parallelt program. Den er fortsatt ekstremt populær i dag, og er fremdeles en av de beste skalerbare minnetildelingsene som er tilgjengelige.
En enkel løsning uten kodeendringer
Ved hjelp av proxy-metodene kan vi erstatte det globalt ny/slett og malloc/calloc/realloc/gratis/etc. rutiner med en dynamisk erstatningsteknikk for minnegrensesnitt. Denne automatiske måten å erstatte standardfunksjonene for dynamisk minnetildeling er uten tvil den mest populære måten å bruke TBBmalloc på. Det er enkelt og tilstrekkelig for de fleste programmer.
Detaljene i mekanismen som brukes på hvert operativsystem varierer litt, men nettoeffekten er den samme overalt.
Vi starter vår 5-minutters prøveversjon med å laste ned og installere Threading Building Blocks (gratis fra //threadingbuildingblocks.org; den er også inkludert som en del av Intel Parallel Studio-produkter).
Bruk proxy på Linux
På Linux kan vi bytte ut enten ved å laste inn proxy-biblioteket ved programbelastningstid ved hjelp av LD_PRELOAD miljøvariabel (uten å endre den kjørbare filen), eller ved å koble den viktigste kjørbare filen til proxy-biblioteket (-ltbbmalloc_proxy). Linux-programlaster må kunne finne proxy-biblioteket og det skalerbare minnetildelingsbiblioteket ved programbelastningstid. For det kan vi inkludere katalogen som inneholder bibliotekene i LD_LIBRARY_PATH miljøvariabel eller legg den til /etc/ld.so.conf.
Prøv som følger:
tid ./a.out (eller hva programmet vårt heter)
eksporter LD_PRELOAD = libtbbmalloc_proxy.so.2
tid ./a.out (eller hva programmet vårt heter)
Bruk proxy på macOS
På macOS kan vi bytte ut enten ved å laste proxy-biblioteket ved programinnlastingstid ved hjelp av DYLD_INSERT_LIBRARIES miljøvariabel (uten å endre den kjørbare filen), eller ved å koble den viktigste kjørbare filen til proxy-biblioteket (-ltbbmalloc_proxy). MacOS-programlaster må kunne finne proxy-biblioteket og det skalerbare minnetildelingsbiblioteket ved programbelastningstid. For det kan vi inkludere katalogen som inneholder bibliotekene i DYLD_LIBRARY_PATH miljøvariabel.
Prøv som følger:
tid ./a.out (eller hva programmet vårt heter)
eksporter DYLD_INSERT_LIBRARIES = $ TBBROOT / lib / libtbbmalloc_proxy.dylib
tid ./a.out (eller hva programmet vårt heter)
Bruk proxy på Windows
På Windows må vi endre den kjørbare filen vår. Vi kan enten tvinge inn proxy-biblioteket ved å legge til et # inkluderer "tbb / tbbmalloc_proxy.h" i kildekoden vår, eller ved å bruke visse koblingsalternativer når du bygger den kjørbare:
For win32:
tbbmalloc_proxy.lib / INKLUDER: "___ TBB_malloc_proxy"
For win64:
tbbmalloc_proxy.lib / INKLUDER: "__ TBB_malloc_proxy"
Windows-programlaster må kunne finne proxy-biblioteket og det skalerbare minnetildelingsbiblioteket ved programbelastningstid. For det kan vi inkludere katalogen som inneholder bibliotekene i STI miljøvariabel. Prøv det ved å bruke Visual Studio "Performance Profiler" for å tidsbestemme programmet med og uten inkluderings- eller koblingsalternativet.
Testing av proxy-bibliotekets bruk med et lite program
Jeg oppfordrer deg til å prøve med ditt eget program som beskrevet ovenfor. Kjør med og uten fullmektigen, og se hvor stor fordel søknaden din får. Applikasjoner med mye parallellitet og mye minnetildeling ser ofte 10-20% boost (jeg har sett en 400% boost en gang også), mens programmer med liten parallellitet eller få tildelinger kanskje ikke ser noen effekt i det hele tatt. Hurtigtestene, beskrevet tidligere, med proxy-biblioteket, vil fortelle deg hvilken kategori applikasjonen din er i.
Jeg har også skrevet et kort program for å illustrere effektene, samt for å gi en enkel måte å kontrollere at ting er installert og fungerer som forventet. Vi kan prøve proxy-biblioteket med et enkelt program:
#inkludere
# inkluderer "tbb / tbb.h"
ved hjelp av navneområdet tbb;
const int N = 1000000;
int main () {
dobbelt * a [N];
parallel_for (0, N-1, [&] (int i) {a [i] = ny dobbel;});
parallel_for (0, N-1, [&] (int i) {slett en [i];});
retur 0;
}
Eksempelprogrammet mitt bruker mye bunkeplass, så “ulimit –s ubegrenset”(Linux / macOS) eller“/ STABEL: 10000000”(Visual Studio: Properties> Configuration Properties> Linker> System> Stack Reserve Size) vil være viktig for å unngå umiddelbare krasj.
Etter kompilering, her er de forskjellige måtene jeg kjørte det lille programmet mitt for å se hastigheten med og uten proxy-biblioteket.
Kjører og timing tbb_mem.cpp på en quadcore virtuell Linux-maskin, jeg så følgende:
% tid ./tbb_mem
ekte 0m0.160s
bruker 0m0.072s
sys 0m0.048s
%
% eksportLD_PRELOAD = $ TBBROOT / lib / libtbbmalloc_proxy.dylib
%
% tid ./tbb_mem
ekte 0m0.043s
bruker 0m0.048s
sys 0m0.028s
Kjører og timing tbb_mem.cpp på en quadcore iMac (macOS), så jeg følgende:
% tid ./tbb_mem
ekte 0m0.046s
bruker 0m0.078s
sys 0m0.053s
%
% eksport DYLD_INSERT_LIBRARIES = $ TBBROOT / lib / libtbbmalloc_proxy.dylib
%
% tid ./tbb_mem
ekte 0m0.019s
bruker 0m0.032s
sys 0m0.009s
På Windows, ved å bruke Visual Studio “Performance Profiler” på en quadcore Intel NUC (Core i7) så jeg tider på 94 ms uten den skalerbare minneprofilen og 50 ms med den (legge til # inkluderer "tbb / tbbmalloc_proxy.h"inn i eksempelprogrammet).
Kompileringshensyn
Personlig har jeg ikke hatt noe problem med kompilatorer som gjør "malloc-optimaliseringer", men teknisk sett vil jeg foreslå at når du kompilerer med programmer som en slik kompilator "malloc-optimalisering" skal deaktiveres. Det kan være lurt å sjekke kompilatordokumentasjonen til favorittkompilatoren din. For eksempel, med Intel-kompilatorer eller gcc, er det best å sende inn følgende flagg:
-fno-innebygd-malloc (på Windows: / Qfno-innebygd-malloc)
-fno-innebygd-calloc (på Windows: / Qfno-innebygd-calloc)
-fno-innebygd-realloc (på Windows: / Qfno-innebygd-realloc)
-fno-innebygd-fri (på Windows: / Qfno-innebygd-fri)
Unnlatelse av å bruke disse flaggene kan ikke forårsake et problem, men det er ikke en dårlig idé å være trygg.
Sammendrag
Å bruke en skalerbar minnetildeler er et viktig element i ethvert parallelt program. Jeg har vist at TBBmalloc enkelt kan injiseres uten å kreve kodeendringer (selv om det å legge til en "inkluderer" på Windows er min favoritt Windows-løsning). Du kan se en fin hastighet med bare 5 minutters arbeid, og du kan enkelt bruke den på flere applikasjoner. På Linux og macOS kan du til og med kunne øke hastigheten på programmene uten å ha kildekoden!
Klikk her for å laste ned en gratis 30-dagers prøveversjon av Intel Parallel Studio XE.