windows - Python multiprocessing ydeevne forbedres kun med kvadratroten af ​​antallet af anvendte kerner

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg forsøger at implementere multiprocessing i Python (Windows Server 2012) og har problemer med at opnå den grad af præstationsforbedring, som jeg forventer. I særdeleshed for et sæt opgaver, der næsten er helt uafhængige, ville jeg forvente en lineær forbedring med yderligere kerner .





Jeg forstår at - især på Windows - der er overhead involveret i åbning af nye processer
import numpy as np
from multiprocessing import Pool, cpu\_count, Manager
import math as m
from functools import partial
from time import time

def check\_prime(num):

    #Assert positive integer value
    if num!=m.floor(num) or num<1:
        print("Input must be a positive integer")
        return None

    #Check divisibility for all possible factors
    prime = True
    for i in range(2,num):
        if num\%i==0: prime=False
    return prime

def cp\_worker(num, L):
    prime = check\_prime(num)
    L.append((num, prime))


def mp\_primes(omag, mp=cpu\_count()):
    with Manager() as manager:
        np.random.seed(0)
        numlist = np.random.randint(10**omag, 10**(omag+1), 100)

        L = manager.list()
        cp\_worker\_ptl = partial(cp\_worker, L=L)

        try:
            pool = Pool(processes=mp)   
            list(pool.imap(cp\_worker\_ptl, numlist))
        except Exception as e:
            print(e)
        finally:
            pool.close() # no more tasks
            pool.join()

        return L


if \_\_name\_\_ == '\_\_main\_\_':
    rt = []
    for i in range(cpu\_count()):
        t0 = time()
        mp\_result = mp\_primes(6, mp=i+1)
        t1 = time()
        rt.append(t1-t0)
        print("Using \%i core(s), run time is \%.2fs" \% (i+1, rt[-1]))
, og at mange quirks af den underliggende kode kan komme i vejen for en ren trend. Men i teorien skal trenden i sidste ende stadig være nær lineær for en fuldt paralleliseret opgave N\_cores=36; eller måske logistisk, hvis jeg havde en del seriel opgave python. [7] [8]


Men når jeg kører multiprocessing.Pool på en primær kontrol test funktion (kode nedenfor), får jeg et næsten perfekt kvadratrotsforhold op til N\_cores=36 (antallet af fysiske kerner på min server) før det forventede resultat, når jeg kommer ind i de ekstra logiske kerner.





Her er et plot af mine resultater testresultater:
Indtast billedbeskrivelse her

1 CPU-kerne ]] divideret med 1 strong> [[ en kørselstid med N CPU-kerner ]] ).

[9] [10]


Er det normalt at have denne dramatiske aftagende afkast med multiprocessing? Eller mangler jeg noget med min implementering?





import numpy as np
from multiprocessing import Pool, cpu\_count, Manager
import math as m
from functools import partial
from time import time

def check\_prime(num):

    #Assert positive integer value
    if num!=m.floor(num) or num<1:
        print("Input must be a positive integer")
        return None

    #Check divisibility for all possible factors
    prime = True
    for i in range(2,num):
        if num\%i==0: prime=False
    return prime

def cp\_worker(num, L):
    prime = check\_prime(num)
    L.append((num, prime))


def mp\_primes(omag, mp=cpu\_count()):
    with Manager() as manager:
        np.random.seed(0)
        numlist = np.random.randint(10**omag, 10**(omag+1), 100)

        L = manager.list()
        cp\_worker\_ptl = partial(cp\_worker, L=L)

        try:
            pool = Pool(processes=mp)   
            list(pool.imap(cp\_worker\_ptl, numlist))
        except Exception as e:
            print(e)
        finally:
            pool.close() # no more tasks
            pool.join()

        return L


if \_\_name\_\_ == '\_\_main\_\_':
    rt = []
    for i in range(cpu\_count()):
        t0 = time()
        mp\_result = mp\_primes(6, mp=i+1)
        t1 = time()
        rt.append(t1-t0)
        print("Using \%i core(s), run time is \%.2fs" \% (i+1, rt[-1]))


Bemærk: Jeg er klar over, at det for denne opgave sandsynligvis vil være mere effektivt at implementere multi threading , men det egentlige script, for hvilket dette er en forenklet analog, er uforenelig med Python multithreading på grund af GIL.

Bedste reference


@KellanM fortjente [[+ 1]] til kvantitativ effektivitetsovervågning



   Mangler jeg noget med min implementering?



Ja, du abstraherer fra alle tilføjelsesomkostninger til processtyring.



Mens du har udtrykt forventning om en lineær forbedring med yderligere kerner. ', ville dette næppe forekomme i praksis af flere årsager (selvom kommunismens hype ikke kunne levere noget gratis ).


Gene AMDAHL har formuleret grundlov for faldende afkast .
Indtast billedbeskrivelse her

En nyere nyformuleret version tog også højde for effekterne af processtyring {setup | terminate} - ekstraomkostninger og forsøgte at klare < strong> atomicity-of-processing
(givet store arbejdspakning nyttelast kan ikke nemt genplaceres/distribueres over tilgængelig pool af gratis CPU-kerner i de fleste almindelige programmeringssystemer (undtagen nogle faktisk specifikke mikroplanlægningskunst, som f.eks. den som er demonstreret i Semantic Design s s PARLANSE eller LLNL sISISAL har vist sig så farveløst i fortiden).





Et bedste næste skridt?



Hvis du virkelig er interesseret i dette domæne, kan man altid eksperimentelt måle og sammenligne de reelle omkostninger ved processtyring (plus datastrømmeomkostninger plus omkostninger til hukommelsesallokering ... indtil afslutningen af ​​processen og resultaterne i hovedsagen proces) for at kvantitativt retfærdigt registrere og evaluere add-on-omkostninger/fordeleforholdet ved at bruge flere CPU-kerner (som vil få i python, geninstallerede hele python-tolk-tilstanden, herunder hele dens hukommelse -Stat, før en første brugbar operation vil blive udført i en første opstart og installationsproces).


Underperformance (for det tidligere tilfælde nedenfor)
hvis ikke katastrofale effekter (fra sidstnævnte tilfælde nedenfor),
af en af ​​de ukorrekte ressourcer-mapping-politikker, er det en under-booking - ressourcer fra en pool af CPU -corer
eller
en over-booking - ressourcer fra en pool af RAM -rummet
diskuteres også her


Linket til Reformuleret Amdahls lov ovenfor vil hjælpe dig med at evaluere punktet af faldende afkast, ikke at betale mere end nogensinde vil modtage.


Hoefinger et Haunschmid-eksperimenter kan tjene som et godt praktisk bevis , hvordan et voksende antal behandlingsnoder (det være sig en lokal O/S-styret CPU-kerne eller en NUMA-distribueret arkitekturknude) vil begynde at falde den resulterende ydeevne,

hvor en Punkt med aftagende afkast (demonstreret i overordnede agnostiske Amdahls lov)

vil faktisk begynde at blive en Punkt, hvorefter du betaler mere end at modtage. :


Indtast billedbeskrivelse her
Held og lykke på dette interessante felt!
Indtast billedbeskrivelse her [15] [16]





Sidst men ikke mindst



NUMA/ikke-lokalitetsproblemer får deres stemme hørt i diskussionen om skalering for HPC-klasses indstillede (in-Cache/in-RAM-databehandlingsstrategier) og kan som en bivirkning hjælpe med at registrere fejlene (som rapporteret af < stærk> @eryksun
ovenfor). Man kan føle sig frit for at gennemgå den faktiske NUMA-topologiens platform ved at bruge værktøjet lstopo for at se den abstraktion, det ene operativsystem forsøger at arbejde sammen med, når man planlægger 'bare' - [CONCURRENT] opgaveudførelse over en sådan NUMA-ressourcer-topologi:


Indtast billedbeskrivelse her [18]