A maioria das funções do Numpy habilitará multithreading por padrão.
por exemplo, eu trabalho em uma estação de trabalho intel cpu de 8 núcleos, se eu executar um script
import numpy as np
x=np.random.random(1000000)
for i in range(100000):
np.sqrt(x)
o linux top
mostrará 800% de uso da CPU durante a execução, como o
que significa que o numpy detecta automaticamente que minha estação de trabalho possui 8 núcleos e np.sqrt
usa automaticamente todos os 8 núcleos para acelerar a computação.
No entanto, eu encontrei um bug estranho. Se eu executar um script
import numpy as np
import pandas as pd
df=pd.DataFrame(np.random.random((10,10)))
df+df
x=np.random.random(1000000)
for i in range(100000):
np.sqrt(x)
o uso da CPU é 100% !!. Isso significa que, se você adicionar dois DataFrame de dois pandas antes de executar qualquer função numpy, o recurso de multithreading automático de numpy desaparecerá sem nenhum aviso! Isso não é absolutamente razoável, por que o cálculo do DataFrame do Pandas afetaria a configuração de segmentação do Numpy? Isso é um bug? Como contornar isso?
PS:
Eu cavo ainda mais usando a perf
ferramenta Linux .
executando o primeiro script mostra
Ao executar o segundo script, mostra
Portanto, ambos os scripts envolvem libmkl_vml_avx2.so
, enquanto o primeiro script envolve outros libiomp5.so
que parecem estar relacionados ao openMP.
E como vml significa intel math math library, de acordo com o vml doc, acho que pelo menos as funções abaixo são automaticamente multithread
import numpy as np import pandas as pd import os os.environ["MKL_NUM_THREADS"] = '4' print(os.environ["MKL_NUM_THREADS"]) df=pd.DataFrame(np.random.random((10,10))) df+df print(os.environ["MKL_NUM_THREADS"]) a = np.random.random((20000000, 3)) b = np.random.random((3, 30)) for _ in range(10): c = np.dot(a, b)
Respostas:
O Pandas usa
numexpr
sob o capô para calcular algumas operações enumexpr
define o número máximo de threads para vml como 1, quando é importado :e é importado pelo pandas quando
df+df
é avaliado em expression.py :No entanto, a distribuição Anaconda também usa VML-funcionalidade para funções tais como
sqrt
,sin
,cos
e assim por diante - e uma veznumexpr
definido o número máximo de VML-fios para um, as numpy-funções não utilização paralelização.O problema pode ser facilmente visto no gdb (usando seu script lento):
ou seja, podemos ver,
numexpr
define o número de threads como 1. Que é usado mais tarde quando a função vml-sqrt é chamada:Então, podemos ver que numpy usa a implementação de vml
vdSqrt
utilizadamkl_vml_serv_threader_d_1i_1o
para decidir se o cálculo deve ser feito em paralelo e parece o número de threads:o registro
%rax
tem o número máximo de threads e é 1.Agora podemos usar
numexpr
para aumentar o número de threads de vml , ou seja:Agora vários núcleos são utilizados!
fonte
numexpr
por trás da cena.Olhando para entorpecido, parece que, sob o capô, ele teve problemas de ativação / desativação com o multithreading e, dependendo da versão que você está usando, você pode começar a ver falhas ao aumentar ne.set_vml_num_threads () ..
http://numpy-discussion.10968.n7.nabble.com/ANN-NumExpr-2-7-0-Release-td47414.html
Eu preciso entender como isso é colado no interpretador python, dado o seu exemplo de código em que parece estar de alguma forma permitindo que várias chamadas aparentemente síncronas / ordenadas ao np.sqrt () prossigam paralelamente. Eu acho que se o intérprete python estiver sempre retornando uma referência a um objeto quando ele aparecer na pilha, e no seu exemplo estiver apenas lançando essas referências e não atribuí-las ou manipulá-las da maneira que seria adequada. Mas se as iterações de loop subsequentes dependem das anteriores, parece menos claro como elas podem ser paralelizadas com segurança. Indiscutivelmente falha silenciosa / resultados errados é um resultado pior do que falhas.
fonte
Eu acho que sua premissa inicial pode estar incorreta -
Você declarou: O que significa que o numpy detecta automaticamente que minha estação de trabalho possui 8 núcleos e o np.sqrt usa automaticamente todos os 8 núcleos para acelerar a computação.
Uma única função np.sqrt () não consegue adivinhar como será chamada ou retornada a seguir antes de ser parcialmente concluída. Existem mecanismos de paralelismo em python, mas nenhum é automático.
Agora, tendo dito isso, o intérprete python pode otimizar o loop for para paralelismo, que pode ser o que você está vendo, mas eu suspeito fortemente que se você olhar para a hora do relógio de parede para esse loop executá-lo, não haverá diferente, independentemente de você estar (aparentemente) usando 8 núcleos ou 1 núcleo.
ATUALIZAÇÃO: Depois de ler um pouco mais dos comentários, parece que o comportamento multinúcleo que você está vendo está relacionado à distribuição anaconda do interpretador python. Dei uma olhada, mas não consegui encontrar nenhum código-fonte, mas parece que a licença python permite que entidades (como anaconda.com) compilem e distribuam derivados do intérprete sem exigir que suas alterações sejam publicadas.
Eu acho que você pode falar com o pessoal da anaconda - o comportamento que você está vendo será difícil de entender sem saber o que / se alguma coisa mudou no intérprete ..
Faça também uma verificação rápida do relógio do relógio de parede com / sem a otimização para ver se é realmente 8x mais rápido - mesmo se você realmente tiver todos os 8 núcleos trabalhando em vez de 1, seria bom saber se os resultados são realmente 8x mais rápido ou se houver spinlocks em uso que ainda estejam serializando em um único mutex.
fonte