Por que o multiprocessamento usa apenas um núcleo depois de importar o numpy?

127

Não tenho certeza se isso conta mais como um problema do sistema operacional, mas pensei em perguntar aqui, caso alguém tenha alguma ideia do final das coisas em Python.

Eu tenho tentado paralelizar um forloop pesado de CPU usando joblib, mas acho que, em vez de cada processo de trabalho ser atribuído a um núcleo diferente, acabo com todos eles sendo atribuídos ao mesmo núcleo e sem ganho de desempenho.

Aqui está um exemplo muito trivial ...

from joblib import Parallel,delayed
import numpy as np

def testfunc(data):
    # some very boneheaded CPU work
    for nn in xrange(1000):
        for ii in data[0,:]:
            for jj in data[1,:]:
                ii*jj

def run(niter=10):
    data = (np.random.randn(2,100) for ii in xrange(niter))
    pool = Parallel(n_jobs=-1,verbose=1,pre_dispatch='all')
    results = pool(delayed(testfunc)(dd) for dd in data)

if __name__ == '__main__':
    run()

... e aqui está o que vejo htopenquanto este script está sendo executado:

htop

Estou executando o Ubuntu 12.10 (3.5.0-26) em um laptop com 4 núcleos. Claramente joblib.Parallelestá gerando processos separados para os diferentes trabalhadores, mas existe alguma maneira de eu fazer esses processos serem executados em núcleos diferentes?

ali_m
fonte
stackoverflow.com/questions/15168014/… - não tenho respostas lá, receio, mas parece o mesmo problema.
NPE
Isso ainda é um problema? Estou tentando recriar isso com Python 3.7 e importando numpy com multiprocessing.Pool (), e ele está usando todos os threads (como deveria). Só quero garantir que isso foi corrigido.
Jared Nielsen

Respostas:

148

Depois de pesquisar um pouco mais, encontrei a resposta aqui .

Acontece que certos módulos Python ( numpy, scipy, tables, pandas, skimage...) mexer com afinidade núcleo na importação. Até onde eu sei, esse problema parece ser causado especificamente por eles vincularem-se a bibliotecas OpenBLAS multithread.

Uma solução alternativa é redefinir a afinidade da tarefa usando

os.system("taskset -p 0xff %d" % os.getpid())

Com esta linha colada após a importação do módulo, meu exemplo agora é executado em todos os núcleos:

htop_workaround

Minha experiência até agora tem sido que isso não parece ter nenhum efeito negativo no numpydesempenho da empresa, embora provavelmente seja específico da máquina e da tarefa.

Atualizar:

Existem também duas maneiras de desativar o comportamento de redefinição de afinidade da CPU do próprio OpenBLAS. No tempo de execução, você pode usar a variável de ambiente OPENBLAS_MAIN_FREE(ou GOTOBLAS_MAIN_FREE), por exemplo

OPENBLAS_MAIN_FREE=1 python myscript.py

Ou, alternativamente, se você estiver compilando o OpenBLAS a partir da fonte, poderá desativá-lo permanentemente no momento da construção, editando o Makefile.rulepara conter a linha

NO_AFFINITY=1
ali_m
fonte
Obrigado, sua solução resolveu o problema. Uma pergunta, eu tenho o mesmo código, mas corro de maneira diferente em duas máquinas diferentes. Ambas as máquinas são Ubuntu 12.04 LTS, python 2.7, mas apenas uma tem esse problema. Tens alguma ideia do porquê?
Iampat 8/13
Ambas as máquinas possuem o OpenBLAS (desenvolvido com o OpenMPI).
Iampat 8/10
2
Tópico antigo, mas no caso de alguém encontrar esse problema, eu tive o problema exato e estava realmente relacionado às bibliotecas do OpenBLAS. Veja aqui duas soluções possíveis e algumas discussões relacionadas.
Gabriel
2
Outra maneira de definir a afinidade da CPU é usarpsutil .
Ioannis Filippidis
2
@JHG É um problema com OpenBLAS em vez de Python, então eu não consigo ver nenhuma razão pela qual a versão Python iria fazer a diferença
ali_m
27

O Python 3 agora expõe os métodos para definir diretamente a afinidade

>>> import os
>>> os.sched_getaffinity(0)
{0, 1, 2, 3}
>>> os.sched_setaffinity(0, {1, 3})
>>> os.sched_getaffinity(0)
{1, 3}
>>> x = {i for i in range(10)}
>>> x
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> os.sched_setaffinity(0, x)
>>> os.sched_getaffinity(0)
{0, 1, 2, 3}
WoJ
fonte
1
Erro> AttributeError: o módulo 'os' não tem nenhum atributo 'sched_getaffinity', Python 3.6
Paddy
4
@Paddy Na documentação vinculada: Eles estão disponíveis apenas em algumas plataformas Unix.
BlackJack
2
Tenho mesmo problema, mas tenho integrar essa mesma linha no topo os.system ( "taskset -p 0xff% d" % os.getpid ()), mas a sua não usa toda a cpu
rajeshcis