2017-06-25 2 views
1

Ich verwende python.multiprocessing.sharedctypes.RawArray, um große Anzahl Arrays zwischen mehreren Prozessen zu teilen. Und ich habe bemerkt, dass, wenn dieses Array groß ist (> 1 oder 2 GB), es sehr langsam initialisiert wird und auch viel langsamer beim Lesen/Schreiben ist (und Lese-/Schreibzeit ist nicht vorhersehbar, manchmal ziemlich schnell, manchmal sehr sehr) langsam).Schreiben in Shared Memory in Python ist sehr langsam

Ich habe ein kleines Beispielskript erstellt, das nur einen Prozess verwendet, ein freigegebenes Array initialisiert und mehrere Male darauf schreibt. Und misst Zeit für diese Operationen.

import argparse 
import ctypes 
import multiprocessing as mp 
import multiprocessing.sharedctypes as mpsc 
import numpy as np 
import time 

def main(): 
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) 
    parser.add_argument('-c', '--block-count', type=int, default=1, 
         help='Number of blocks to write') 
    parser.add_argument('-w', '--block-width', type=int, default=20000, 
         help='Block width') 
    parser.add_argument('-d', '--block-depth', type=int, default=15000, 
         help='Block depth') 
    args = parser.parse_args() 
    blocks = args.block_count 
    blockwidth = args.block_width 
    depth = args.block_depth 
    start = time.perf_counter() 
    shared_array = mpsc.RawArray(ctypes.c_uint16, blocks*blockwidth*depth) 
    finish = time.perf_counter() 
    print('Init shared array of size {:.2f} Gb: {:.2f} s'.format(blocks*blockwidth*depth*ctypes.sizeof(ctypes.c_uint16)/1024/1024/1024, (finish-start))) 
    numpy_array = np.ctypeslib.as_array(shared_array).reshape(blocks*blockwidth, depth) 
    start = time.perf_counter() 
    for i in range(blocks): 
     begin = time.perf_counter() 
     numpy_array[i*blockwidth:(i+1)*blockwidth, :] = np.ones((blockwidth, depth), dtype=np.uint16) 
     end = time.perf_counter() 
     print('Write = %.2f s' % (end-begin)) 
    finish = time.perf_counter() 
    print('Total time = %.2f s' % (finish-start)) 

if __name__ == '__main__': 
    main() 

Wenn ich diesen Code ausführen ich folgende auf meinem PC erhalten:

$ python shared-minimal.py -c 1 
Init shared array of size 0.56 Gb: 0.36 s 
Write = 0.13 s 
Total time = 0.13 s 
$ python shared-minimal.py -c 2 
Init shared array of size 1.12 Gb: 0.72 s 
Write = 0.12 s 
Write = 0.13 s 
Total time = 0.25 s 
$ python shared-minimal.py -c 4 
Init shared array of size 2.24 Gb: 5.40 s 
Write = 1.17 s 
Write = 1.17 s 
Write = 1.17 s 
Write = 1.57 s 
Total time = 5.08 s 

Im letzten Fall, wenn Array-Größe mehr als 2 GB ist, ist die Initialisierung Zeit nicht linear abhängig von Array-Größe und das Zuweisen von Sicherungsgrößen-Slices zum Array ist mehr als 5-mal langsamer.

Ich frage mich, warum das passiert. Ich benutze das Skript unter Ubuntu 16.04 mit Python 3.5. Ich habe auch bemerkt, indem iotop, dass beim Initialisieren und Schreiben in das Array gibt es eine Platte schreiben Aktivität mit der gleichen Größe wie Shared Array, aber ich bin mir nicht sicher, ob eine echte Datei erstellt wird oder es ist nur im Speicherbetrieb (I angenommen, es sollte sein). Im Allgemeinen wird mein System weniger reaktionsfähig, im Falle eines großen gemeinsamen Arrays. Es wird nicht getauscht, geprüft mit top, ipcs -mu und vmstat.

+2

Verfügt Ihr System über 2 GB physischen Speicher? Es klingt, als würde es tauschen. – pvg

+0

@pvg es hat 16 GB Speicher, und ich habe auch auf einem Computer mit 64 GB Speicher getestet. Die Ergebnisse sind nicht immer die gleichen, aber auf dem Computer mit 16 GB physischen Speicher beginnt es erheblich langsamer zu verlangsamen, wenn Shared Array mehr als ~ 1,5 GB ist, und auf der Maschine mit 64 GB Shared Array ist mehr als ~ 6,5 GB. Außerdem habe ich versucht, "top" und "ipcs -mu" auszuführen, und sie zeigen keine Änderung der Swap-Nutzung. –

+0

Sie möchten wahrscheinlich etwas wie 'vmstat' oder irgendetwas anderes, das Sie tatsächliche Swap-Ins und Outs statt Nutzung verfolgen können. Speicherzugriffszeiten können sich aus verschiedenen Gründen nichtlinear ändern, aber es ist schwierig, den Plattenzugriff und das nicht reagierende System durch etwas anderes als das Tauschen zu erklären. – pvg

Antwort

1

Nach mehr Forschung Ich habe festgestellt, dass Python tatsächlich schafft Ordner in /tmp, die mit pymp- beginnen werden, und obwohl keine Dateien in sie sichtbar sind Datei-Viewer verwenden, es sieht aus wie exatly /tmp/ von Python für gemeinsam genutzten Speicher verwendet wird. Die Leistung scheint zu sinken, wenn Dateikassetten geleert werden.

Die Arbeitslösung am Ende war /tmp als tmpfs zu montieren:

sudo mount -t tmpfs tmpfs /tmp 

Und wenn mit den neuesten Docker, indem --tmpfs /tmp Argument für den Befehl docker run.

Danach werden Lese-/Schreibvorgänge im RAM ausgeführt, und die Leistung ist schnell und stabil.

Ich frage mich immer noch, warum /tmp für den gemeinsamen Speicher verwendet wird, nicht /dev/shm, die bereits als tmpfs monted ist und soll für den gemeinsamen Speicher verwendet werden.