Programmering

Hvorfor effektiv parallell programmering må inkludere skalerbar minnetildeling

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.

$config[zx-auto] not found$config[zx-overlay] not found