2016-01-21 3 views
9

NumPy Gli array sono eccezionali sia per le prestazioni che per l'utilizzo semplice (semplificazione delle operazioni di taglio, indicizzazione rispetto agli elenchi).Accelerare l'array NumPy strutturato

Provo a costruire un contenitore di dati da un NumPy structured array anziché dict di NumPy arrays. Il problema è che le prestazioni sono molto peggiori. Circa 2,5 volte tanto male utilizzando dati omogenei e circa 32 volte per dati eterogenei (sto parlando dei tipi di dati NumPy).

C'è un modo per velocizzare l'array strutturato? Ho provato a cambiare il memoryorder da 'c' a 'f', ma questo non ha avuto alcun effetto.

Ecco il mio codice profiling:

import time 
import numpy as np 

NP_SIZE = 100000 
N_REP = 100 

np_homo = np.zeros(NP_SIZE, dtype=[('a', np.double), ('b', np.double)], order='c') 
np_hetro = np.zeros(NP_SIZE, dtype=[('a', np.double), ('b', np.int32)], order='c') 
dict_homo = {'a': np.zeros(NP_SIZE), 'b': np.zeros(NP_SIZE)} 
dict_hetro = {'a': np.zeros(NP_SIZE), 'b': np.zeros(NP_SIZE, np.int32)} 

t0 = time.time() 
for i in range(N_REP): 
    np_homo['a'] += i 

t1 = time.time() 
for i in range(N_REP): 
    np_hetro['a'] += i 

t2 = time.time() 
for i in range(N_REP): 
    dict_homo['a'] += i 

t3 = time.time() 
for i in range(N_REP): 
    dict_hetro['a'] += i 
t4 = time.time() 

print('Homogeneous Numpy struct array took {:.4f}s'.format(t1 - t0)) 
print('Hetoregeneous Numpy struct array took {:.4f}s'.format(t2 - t1)) 
print('Homogeneous Dict of numpy arrays took {:.4f}s'.format(t3 - t2)) 
print('Hetoregeneous Dict of numpy arrays took {:.4f}s'.format(t4 - t3)) 

Edit: Ho dimenticato di mettere i miei valori di temporizzazione:

Homogenious Numpy struct array took 0.0101s 
Hetoregenious Numpy struct array took 0.1367s 
Homogenious Dict of numpy arrays took 0.0042s 
Hetoregenious Dict of numpy arrays took 0.0042s 

Edit2: ho aggiunto qualche banco di prova supplementare con il modulo timit:

import numpy as np 
import timeit 

NP_SIZE = 1000000 

def time(data, txt, n_rep=1000): 
    def intern(): 
     data['a'] += 1 

    time = timeit.timeit(intern, number=n_rep) 
    print('{} {:.4f}'.format(txt, time)) 


np_homo = np.zeros(NP_SIZE, dtype=[('a', np.double), ('b', np.double)], order='c') 
np_hetro = np.zeros(NP_SIZE, dtype=[('a', np.double), ('b', np.int32)], order='c') 
dict_homo = {'a': np.zeros(NP_SIZE), 'b': np.zeros(NP_SIZE)} 
dict_hetro = {'a': np.zeros(NP_SIZE), 'b': np.zeros(NP_SIZE, np.int32)} 

time(np_homo, 'Homogeneous Numpy struct array') 
time(np_hetro, 'Hetoregeneous Numpy struct array') 
time(dict_homo, 'Homogeneous Dict of numpy arrays') 
time(dict_hetro, 'Hetoregeneous Dict of numpy arrays') 

in:

Homogeneous Numpy struct array 0.7989 
Hetoregeneous Numpy struct array 13.5253 
Homogeneous Dict of numpy arrays 0.3750 
Hetoregeneous Dict of numpy arrays 0.3744 

I rapporti tra le piste sembrano ragionevolmente stabili. Utilizzando entrambi i metodi e una diversa dimensione dell'array.

Per l'offcase è importante: pitone: 3.4 NumPy: 1.9.2

+2

Poiché questa domanda richiede un problema di prestazioni specifico con NumPy piuttosto che una critica generale, è stata eseguita la migrazione da Code Review a Stack Overflow. –

+0

Se si vuole veramente lavorare con array strutturati, suggerirei di dare una prova a [panda] (http://pandas.pydata.org/). –

+1

Vedere questo numero: https://github.com/numpy/numpy/issues/6467 – MaxNoe

risposta

2

Nella mia tempistica rapida verifica la differenza non è grande:

In [717]: dict_homo = {'a': np.zeros(10000), 'b': np.zeros(10000)} 
In [718]: timeit dict_homo['a']+=1 
10000 loops, best of 3: 25.9 µs per loop 
In [719]: np_homo = np.zeros(10000, dtype=[('a', np.double), ('b', np.double)]) 
In [720]: timeit np_homo['a'] += 1 
10000 loops, best of 3: 29.3 µs per loop 

Nel caso dict_homo, il il fatto che l'array sia incorporato in un dizionario è un punto secondario. L'accesso al dizionario semplice come questo è veloce, praticamente uguale all'accesso alla matrice per nome della variabile.

Quindi il primo caso è fondamentalmente un test di += per un array 1d.

Nel caso strutturato, i valori a e si alternano nel buffer di dati, pertanto np_homo['a'] è una vista che "estrae" i numeri alternativi. Quindi non sorprende che sarebbe un po 'più lento.

In [721]: np_homo 
Out[721]: 
array([(41111.0, 0.0), (41111.0, 0.0), (41111.0, 0.0), ..., (41111.0, 0.0), 
     (41111.0, 0.0), (41111.0, 0.0)], 
     dtype=[('a', '<f8'), ('b', '<f8')]) 

Un array 2d interlaccia anche i valori della colonna.

In [722]: np_twod=np.zeros((10000,2), np.double) 
In [723]: timeit np_twod[:,0]+=1 
10000 loops, best of 3: 36.8 µs per loop 

Sorprendentemente è in realtà un po 'più lento del caso strutturato. L'utilizzo della forma order='F' o (2.10000) lo accelera un po ', ma non è ancora buono come il caso strutturato.

Questi sono tempi di prova piccoli, quindi non farò grandi pretese. Ma l'array strutturato non guarda indietro.


Un altro test di tempo, l'inizializzazione del array o dizionario fresca ogni passo

In [730]: %%timeit np.twod=np.zeros((10000,2), np.double) 
np.twod[:,0] += 1 
    .....: 
10000 loops, best of 3: 36.7 µs per loop 
In [731]: %%timeit np_homo = np.zeros(10000, dtype=[('a', np.double), ('b', np.double)]) 
np_homo['a'] += 1 
    .....: 
10000 loops, best of 3: 38.3 µs per loop 
In [732]: %%timeit dict_homo = {'a': np.zeros(10000), 'b': np.zeros(10000)} 
dict_homo['a'] += 1 
    .....: 
10000 loops, best of 3: 25.4 µs per loop 

2d e strutturato sono più vicini, con un po 'meglio le prestazioni per il dizionario (1d) caso. Ho provato questo con np.ones, dal momento che np.zeros può avere un'allocazione ritardata, ma nessuna differenza di comportamento.

+0

Hmm. Interessante. Soprattutto i primi risultati. Hai provato ad aumentare la dimensione degli elementi? Giusto per essere sicuro che il tempo necessario non sia dominato da qualche costante. –