Темы Python не улучшают скорость

Чтобы ускорить определенную логику обработки списка, я написал декоратор, который будет 1) перехватывать входящий вызов функции 2) принимать его входной список, разбивать его на несколько частей 4) передавать эти части исходной функции в отдельных потоках 5) объединять вывод и возврат

Я думал, что это была довольно изящная идея, пока я не закодировал ее и не увидел, что скорость не изменилась! Несмотря на то, что на htop работают несколько ядер, многопоточная версия на самом деле медленнее, чем однопоточная.

Связано ли это с печально известным cpython GIL?

Спасибо!

from threading import Thread 
import numpy as np 
import time

# breaks a list into n list of lists
def split(a, n):
    k, m = len(a) / n, len(a) % n
    return (a[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in xrange(n))

THREAD_NUM = 8 

def parallel_compute(fn):
    class Worker(Thread):
        def __init__(self, *args):
            Thread.__init__(self)
            self.result = None
            self.args = args
        def run(self):
            self.result = fn(*self.args)
    def new_compute(*args, **kwargs):        
        threads = [Worker(args[0], args[1], args[2], x) for x in split(args[3], THREAD_NUM)]
        for x in threads: x.start()
        for x in threads: x.join()
        final_res = []
        for x in threads: final_res.extend(x.result)
        return final_res        
    return new_compute

# some function that does a lot of computation
def f(x): return np.abs(np.tan(np.cos(np.sqrt(x**2))))

class Foo:
    @parallel_compute
    def compute(self, bla, blah, input_list):
        return map(f, input_list)

inp = [i for i in range(40*1000*100)]
#inp = [1,2,3,4,5,6,7]

if __name__ == "__main__": 

    o = Foo()
    start = time.time()
    res = o.compute(None, None, inp)
    end = time.time()
    print 'parallel', end - start

Однопоточная версия

import time, fast_one, numpy as np

class SlowFoo:
    def compute(self, bla, blah, input_list):
        return map(fast_one.f, input_list)

if __name__ == "__main__": 

    o = SlowFoo()
    start = time.time()
    res = np.array(o.compute(None, None, fast_one.inp))
    end = time.time()
    print 'single', end - start

А вот и многопроцессорная версия, которая дает "PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed".

import pathos.multiprocessing as mp
import numpy as np, dill
import time

def split(a, n):
    k, m = len(a) / n, len(a) % n
    return (a[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in xrange(n))

def f(x): return np.abs(np.tan(np.cos(np.sqrt(x**2))))

def compute(input_list):
    return map(f, input_list)

D = 2; pool = mp.Pool(D)
def parallel_compute(fn):
    def new_compute(*args, **kwargs):
        inp = []
        for x in split(args[0], D): inp.append(x)
        outputs_async = pool.map_async(fn, inp)
        outputs = outputs_async.get()
        outputs = [y for x in outputs for y in x]
        return outputs
    return new_compute

compute = parallel_compute(compute)

inp = [i for i in range(40*1000)]

if __name__ == "__main__": 

    start = time.time()
    res = compute(inp)
    end = time.time()
    print 'parallel', end - start
    print len(res)

2 ответа

Решение

Да, когда ваши потоки выполняют работу с привязкой к процессору, реализованную в Python (не с помощью, скажем, расширений C, которые могут освобождать GIL до и после маршалинга / демаршаллинга данных из структур Python), GIL является проблемой здесь.

Я бы предложил использовать многопроцессорную модель, реализацию Python, в которой ее нет (IronPython, Jython и т. Д.), Или другой язык в целом (если вы выполняете работу, чувствительную к производительности, нет конца языкам, столь же гибким) как Python, но со значительно лучшей производительностью во время выполнения).

В качестве альтернативы вы можете повторно подписать и запустить весь параллельный код в подпроцессах.

Вам нужны рабочие потоки, которые запускают подпроцесс для расчета. Эти подпроцессы могут работать действительно параллельно.

Другие вопросы по тегам