Programmering

Hvorfor effektiv parallel programmering skal omfatte skalerbar hukommelsesallokering

Multicore processor? Ja.

Skrive program, der skal køre parallelt? Ja.

Huskede du at bruge en skalerbar hukommelsesallokator? Ingen? Læs derefter videre ...

Efter min erfaring er at sørge for, at "hukommelsestildeling" til et program er klar til parallelisme, et ofte overset element for at få et parallelt program til at fungere godt. Jeg kan vise dig en utrolig nem måde at se, om dette er et problem for et kompileret program (C, C ++, Fortran osv.) - samt hvordan man løser det.

En kritisk del af ethvert parallelt program er skalerbar hukommelsesallokering, som inkluderer brug afnysåvel som eksplicitte opkald tilmalloc, calloc eller realloc. Valgmulighederne inkluderer TBBmalloc (Intel Threading Building Blocks), jemalloc og tcmalloc. TBBmalloc har en ny "proxy" -funktion, der gør det let at prøve på mindre end 5 minutter på ethvert kompileret program.

Ydelsesfordelene ved at bruge en skalerbar hukommelsesalloker er betydelige. TBBmalloc var blandt de første meget anvendte skalerbare hukommelsesallokatorer, ikke mindst fordi det kom gratis med TBB for at hjælpe med at fremhæve vigtigheden af ​​at medtage hukommelsesallokeringsovervejelser i ethvert parallelprogram. Det forbliver ekstremt populært i dag og er stadig en af ​​de bedste skalerbare hukommelsesallokatorer, der er tilgængelige.

En nem løsning uden kodeændringer

Ved hjælp af proxy-metoderne kan vi udskifte globalt ny/slet og malloc/calloc/realloc/ledig/etc. rutiner med en dynamisk erstatningsteknologi til hukommelsesgrænseflade. Denne automatiske måde at erstatte standardfunktionerne til dynamisk hukommelsestildeling på er langt den mest populære måde at bruge TBBmalloc på. Det er let og tilstrækkeligt til de fleste programmer.

Detaljerne i mekanismen, der anvendes på hvert operativsystem, varierer lidt, men nettoeffekten er den samme overalt.

Vi starter vores 5-minutters prøveversion ved at downloade og installere Threading Building Blocks (gratis fra //threadingbuildingblocks.org; det er også inkluderet som en del af Intel Parallel Studio-produkter).

Brug proxy på Linux

På Linux kan vi udskifte enten ved at indlæse proxybiblioteket ved programindlæsningstid ved hjælp af LD_PRELOAD miljøvariabel (uden at ændre den eksekverbare fil) eller ved at linke den primære eksekverbare fil til proxybiblioteket (-ltbbmalloc_proxy). Linux-programindlæseren skal kunne finde proxy-biblioteket og det skalerbare hukommelsesallokeringsbibliotek ved programindlæsningstid. Til det kan vi inkludere biblioteket, der indeholder bibliotekerne i LD_LIBRARY_PATH miljøvariabel eller tilføj den til /etc/ld.so.conf.

Prøv som følger:

tid ./a.out (eller hvad vores program hedder)

eksporter LD_PRELOAD = libtbbmalloc_proxy.so.2

tid ./a.out (eller hvad vores program hedder)

Brug proxy på macOS

På macOS kan vi udskifte enten ved at indlæse proxybiblioteket ved programindlæsningstid ved hjælp af DYLD_INSERT_LIBRARIES miljøvariabel (uden at ændre den eksekverbare fil) eller ved at linke den primære eksekverbare fil til proxybiblioteket (-ltbbmalloc_proxy). MacOS-programindlæseren skal kunne finde proxy-biblioteket og det skalerbare hukommelsesallokeringsbibliotek ved programindlæsningstid. Til det kan vi inkludere biblioteket, der indeholder bibliotekerne i DYLD_LIBRARY_PATH miljøvariabel.

Prøv som følger:

tid ./a.out (eller hvad vores program hedder)

eksporter DYLD_INSERT_LIBRARIES = $ TBBROOT / lib / libtbbmalloc_proxy.dylib

tid ./a.out (eller hvad vores program hedder)

Brug proxy på Windows

På Windows skal vi ændre vores eksekverbare. Vi kan enten tvinge proxy-biblioteket til at blive indlæst ved at tilføje et # inkluderer "tbb / tbbmalloc_proxy.h" i vores kildekode eller ved hjælp af visse linkerindstillinger, når du bygger den eksekverbare:

Til win32:

            tbbmalloc_proxy.lib / INKLUDER: "___ TBB_malloc_proxy"

Til win64:

            tbbmalloc_proxy.lib / INKLUDER: "__ TBB_malloc_proxy"

Windows-programindlæseren skal kunne finde proxy-biblioteket og det skalerbare hukommelsesallokeringsbibliotek ved programindlæsningstid. Til det kan vi inkludere biblioteket, der indeholder bibliotekerne i STI miljøvariabel. Prøv det ved at bruge Visual Studio "Performance Profiler" til at tidsindstille programmet med og uden inkluderings- eller linkmuligheden.

Test af vores proxy-biblioteksbrug med et lille program

Jeg opfordrer dig til at prøve dit eget program som beskrevet ovenfor. Kør med og uden proxyen, og se, hvor stor fordel din ansøgning får. Applikationer med masser af parallelisme og masser af hukommelsesallokering ser ofte 10-20% boosts (jeg har også set en 400% boost en gang), mens programmer med lidt parallelisme eller få tildelinger slet ikke ser nogen effekt. Hurtigtestene, beskrevet tidligere med proxy-biblioteket, fortæller dig, hvilken kategori din applikation er i.

Jeg har også skrevet et kort program for at illustrere effekterne samt for at give en nem måde at kontrollere, at tingene er installeret og fungerer som forventet. Vi kan prøve proxy-biblioteket med et simpelt program:

#omfatte

# inkluderer "tbb / tbb.h"

ved hjælp af namespace tbb;

const int N = 1000000;

int main () {

dobbelt * a [N];

parallel_for (0, N-1, [&] (int i) {a [i] = ny dobbelt;});

parallel_for (0, N-1, [&] (int i) {slet en [i];});

returnere 0;

}

Mit eksempelprogram bruger meget stakplads, så “ulimit –s ubegrænset”(Linux / macOS) eller“/ STABEL: 10000000”(Visual Studio: Egenskaber> Konfigurationsegenskaber> Linker> System> Stakreserverstørrelse) er vigtig for at undgå øjeblikkelige nedbrud.

Efter kompilering er her de forskellige måder, jeg kørte mit lille program for at se hastigheden med og uden proxy-biblioteket.

Kørsel og timing af tbb_mem.cpp på en quadcore virtuel Linux-maskine, jeg så følgende:

% tid ./tbb_mem

rigtige 0m0.160s

bruger 0m0.072s

sys 0m0.048s

%

% eksportLD_PRELOAD = $ TBBROOT / lib / libtbbmalloc_proxy.dylib

%

% tid ./tbb_mem

rigtige 0m0.043s

bruger 0m0.048s

sys 0m0.028s

Kørsel og timing af tbb_mem.cpp på en quadcore iMac (macOS) så jeg følgende:

% tid ./tbb_mem

rigtige 0m0.046s

bruger 0m0.078s

sys 0m0.053s

%

% eksport DYLD_INSERT_LIBRARIES = $ TBBROOT / lib / libtbbmalloc_proxy.dylib

%

% tid ./tbb_mem

rigtige 0m0.019s

bruger 0m0.032s

sys 0m0.009s

På Windows ved hjælp af Visual Studio “Performance Profiler” på en quadcore Intel NUC (Core i7) så jeg tider på 94 ms uden den skalerbare hukommelsesprofil og 50 ms med den (tilføjelse # inkluderer "tbb / tbbmalloc_proxy.h"ind i eksempelprogrammet).

Overvejelser om kompilering

Personligt har jeg ikke haft et problem med kompilatorer, der udfører "malloc-optimeringer", men teknisk set vil jeg foreslå, at når kompilering med programmer, at en sådan compiler "malloc-optimering" skal deaktiveres. Det kan være klogt at kontrollere compilerdokumentationen for din yndlingscompiler. For eksempel med Intel-compilere eller gcc er det bedst at sende følgende flag:

-fno-builtin-malloc (på Windows: / Qfno-builtin-malloc)

-fno-builtin-calloc (på Windows: / Qfno-builtin-calloc)

-fno-builtin-realloc (på Windows: / Qfno-builtin-realloc)

-fno-builtin-free (på Windows: / Qfno-builtin-free)

Manglende brug af disse flag kan muligvis ikke forårsage et problem, men det er ikke en dårlig idé at være sikker.

Resumé

Brug af en skalerbar hukommelsesalloker er et væsentligt element i ethvert parallelt program. Jeg har vist, at TBBmalloc let kan injiceres uden at kræve kodeændringer (selvom tilføjelse af en "inkluder" på Windows er min foretrukne Windows-løsning). Du kan muligvis se en god hastighed med kun 5 minutters arbejde, og du kan nemt anvende den på flere applikationer. På Linux og macOS kan du muligvis fremskynde programmer uden at have kildekoden!

Klik her for at downloade din gratis 30-dages prøveversion af Intel Parallel Studio XE.