PicklingError при использовании многопроцессорной обработки

У меня проблемы при использовании Pool.map_async () (а также Pool.map ()) в модуле многопроцессорности . Я реализовал функцию параллельного цикла, которая отлично работает, пока функция, вводимая в Pool.map_async, является «обычной» функцией. Когда функция, например, метод к классу, то я получаю PicklingError:

cPickle.PicklingError: Can't pickle : attribute lookup __builtin__.function failed

Я использую Python только для научных вычислений, поэтому я не так хорошо знаком с концепцией травления, только что немного узнал об этом сегодня. Я просмотрел несколько предыдущих ответов, например Can't pickle при использовании многопроцессорной обработки Pool.map () Python, но я не могу понять, как заставить его работать, даже если по ссылке, приведенной в ответе.

Мой код, цель которого состояла в том, чтобы смоделировать вектор нормальных rv с использованием нескольких ядер. Обратите внимание, что это всего лишь пример, и, возможно, запуск на нескольких ядрах даже не окупается.

import multiprocessing as mp
import scipy as sp
import scipy.stats as spstat

def parfor(func, args, static_arg = None, nWorkers = 8, chunksize = None):
    """
    Purpose: Evaluate function using Multiple cores.

    Input:
        func       - Function to evaluate in parallel
        arg        - Array of arguments to evaluate func(arg)  
        static_arg - The "static" argument (if any), i.e. the variables that are      constant in the evaluation of func.
        nWorkers   - Number of Workers to process computations.
    Output:
        func(i, static_arg) for i in args.

    """
    # Prepare arguments for func: Collect arguments with static argument (if any)
    if static_arg != None:
        arguments = [[arg] + static_arg for arg in list(args)]
    else:
        arguments = args

    # Initialize workers
    pool = mp.Pool(processes = nWorkers) 

    # Evaluate function
    result = pool.map_async(func, arguments, chunksize = chunksize)
    pool.close()
    pool.join()

    return sp.array(result.get()).flatten() 

# First test-function. Freeze location and scale for the Normal random variates generator.
# This returns a function that is a method of the class Norm_gen. Methods cannot be pickled
# so this will give an error.
def genNorm(loc, scale):
    def subfunc(a):
        return spstat.norm.rvs(loc = loc, scale = scale, size = a)
    return subfunc

# Second test-function. The same as above but does not return a method of a class. This is a "plain" function and can be 
# pickled
def test(fargs):
    x, a, b = fargs
    return spstat.norm.rvs(size = x, loc = a, scale = b)

# Try it out.
N = 1000000

# Set arguments to function. args1 = [1, 1, 1,... ,1], the purpose is just to generate a random variable of size 1 for each 
# element in the output vector.
args1 = sp.ones(N)
static_arg = [0, 1] # standarized normal.

# This gives the PicklingError
func = genNorm(*static_arg)
sim = parfor(func, args1, static_arg = None, nWorkers = 12, chunksize = None)

# This is OK:
func = test
sim = parfor(func, args1, static_arg = static_arg, nWorkers = 12, chunksize = None)

Следуя ссылке, приведенной в ответе на вопрос в , Can't pickle при использовании python multiprocessing Pool.map () , Стивен Бетхард (почти в конце) предлагает использовать модуль copy_reg. Его код:

def _pickle_method(method):
    func_name = method.im_func.__name__
    obj = method.im_self
    cls = method.im_class
    return _unpickle_method, (func_name, obj, cls)

def _unpickle_method(func_name, obj, cls):
    for cls in cls.mro():
        try:
            func = cls.__dict__[func_name]
        except KeyError:
            pass
        else:
            break
    return func.__get__(obj, cls)

import copy_reg
import types

copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method)

Я действительно не понимаю, как я могу это использовать. Единственное, что я мог придумать, это поместить его непосредственно перед кодом, но это не помогло. Простое решение, конечно же, - просто выбрать тот, который работает, и не связываться с copy_reg. Меня больше интересует правильная работа copy_reg, чтобы в полной мере использовать преимущества многопроцессорности без необходимости каждый раз обходить проблему.

Спасибо за вашу помощь, мы очень признательны.

Матиас

26
задан Community 23 May 2017 в 12:16
поделиться