Mam wiele rzadkich matryc scipy (obecnie w formacie CSR), które muszę pomnożyć za pomocą gęstego, numpy wektora 1D. wektor nazywa G
:Scipy Sparse Matrix - Mnożenie wektorów gęstych - Blocks vs Large Matrix
print G.shape, G.dtype
(2097152,) complex64
Każda macierz rzadką ma kształt (16384,2097152)
i jest bardzo rzadki. Gęstość wynosi około 4,0-6. Mam listę 100 tych rzadkich macierzy o nazwie spmats
.
mogę łatwo mnożenie każdej matrycy z G
tak:
res = [spmat.dot(G) for spmat in spmats]
Powoduje listy gęstych wektory kształtu (16384,)
prawidłowo.
My stosowanie jest dość perfomance krytyczne, a więc próbuje się naprzemiennie, co jest najpierw łączyć wszystkie rzadkich matryc w jednej dużej sparce matrycy, a następnie za pomocą tylko jednego połączenia z dot()
tak:
import scipy.sparse as sp
SPMAT = sp.vstack(spmats, format='csr')
RES = SPMAT.dot(G)
Powoduje to, że jeden długi wektor RES
ma kształt (1638400,)
i jest połączoną wersją wszystkich wektorów wynikowych w powyższej res
, zgodnie z oczekiwaniami. Sprawdziłem, czy wyniki są identyczne.
Może jestem całkowicie w błędzie, ale spodziewałem się, że drugi przypadek powinien być szybszy od pierwszego, ponieważ istnieje znacznie mniej numpy, alokacja pamięci, tworzenie obiektów Pythona, pętle Pythona, itp. dbają o czas wymagany do połączenia rzadkich macierzy, a jedynie czas na obliczenie wyniku. Według %timeit
jednak:
%timeit res = [spmat.dot(G) for spmat in spmats]
10 loops, best of 3: 91.5 ms per loop
%timeit RES = SPMAT.dot(G)
1 loops, best of 3: 389 ms per loop
Sprawdziłem, że nie uciekam z pamięci w każdej pracy, i nic podejrzany wydaje się dzieje. Czy jestem szalony, czy to naprawdę dziwne? Czy oznacza to, że wszystkie rzadkie produkty matrycowo-wektorowe powinny być wykonywane w blokach, kilka wierszy na raz, aby były szybsze? O ile rozumiem, rzadki czas mnożenia macierzy gęstym wektorem powinien być liniowy w liczbie niezerowych elementów, który nie zmienia się w dwóch powyższych przypadkach. Co może powodować taką różnicę?
biegnę na pojedynczym rdzeniem maszynie Linux z 4GB RAM, używając EPD7.3
EDIT:
Oto mały przykład, który reprodukuje problem dla mnie:
import scipy.sparse as sp
import numpy as n
G = n.random.rand(128**3) + 1.0j*n.random.rand(128**3)
spmats = [sp.rand (128**2, 128**3, density = 4e-6, format = 'csr', dtype=float64) for i in range(100)]
SPMAT = sp.vstack(spmats, format='csr')
%timeit res = [spmat.dot(G) for spmat in spmats]
%timeit RES = SPMAT.dot(G)
Otrzymuję:
1 loops, best of 3: 704 ms per loop
1 loops, best of 3: 1.34 s per loop
Różnica wydajności w tym przypadku nie jest tak duża jak z moimi własnymi rzadkimi macierzami, które mają pewną strukturę (prawdopodobnie z powodu buforowania), ale jeszcze gorsze jest łączenie macierzy.
Próbowałem z obu scipy 10.1 i 12.0.
Nie mogę tego odtworzyć: produkt z pojedynczą kropką jest dla mnie 5 razy szybszy. Ponieważ starannie starałem się robić to, co opisujesz, czy możesz opublikować minimalny przykład roboczy, aby upewnić się, że robimy to samo? – jorgeca
@jorgeca Dzięki za poświęcenie czasu, aby spróbować i odtworzyć problem. Właśnie zredagowałem moje pytanie z działającym przykładem tego, co robię. –
Dzięki. Nie mogę odtworzyć twoich wyników (w scipy 0.12), ale dla mnie zrozumienie listy jest 5x (!) Wolniejsze, gdy 'G' ma' dtype = np.complex64', jak powiedziałeś, a oba podejścia są równie szybkie, gdy 'G' ma 'dtype = np.complex128'. – jorgeca