在相同范围内调用时,PLINQ AsParallel函数如何将数据传递给函数

时间:2019-06-16 21:52:01

标签: .net ironpython plinq

使用IronPython,我正在并行调用某个函数,该函数位于并行化数据所在的同一函数内,以将其保持在同一范围内。

在CPython的多处理中,很明显,必须将数据显式传递给子流程,打开多少个子流程,等等。这使得易于理解开销。

对于PLINQ,代码如何并行运行?即:

是否有另一个Ironpython实例正在运行,并且所有内容都再次导入?例如,import myHugeLibrary将在每次创建文件的新python实例时运行。

CalcParallel()接收一些数据数组和一个字典。在此范围内的是应并行运行的函数computation(),它在主脚本中调用了另一个函数checkVals()。由于computation()与调用AsParallel()的作用域相同,因此我不需要显式传递要使用的数据。但是,这是否意味着将数据复制到每个进程/线程,或保留为引用,并且仅在读取(而不是写入)时可以使用?如果已复制,则是在每次计算一个项目时复制一次,也就是说,如果列表中有100个项目且有10个线程,它将把数据放入10个块中,从而将数据复制10次,还是将其复制10次? ?

同样,示例C_dict数据在计算出一定数量的数据之后并在运行下一轮数据之前进行修改(基于结果,这增加了许多工作要做)。在并行进程运行时,是否会再次复制此修改后的数据?

下面是我想知道的一些示例代码结构。它不是真正关于代码本身的,但是我写这个只是为了说明问题,即使它不是正确的方式。

# get LINQ dependencies
import clr
clr.AddReference("System.Core")
import System
clr.ImportExtensions(System.Linq)
from System.Threading.Tasks import *

#import some huge library that takes time
import myHugeLibrary

max_val = 4 #some global value used within the thread

def checkVals(itemToCheck,A_vals,B_vals):
    #check against some global value
    if itemToCheck < max_val:
        return 0
    #do something else with A_vals

def CalcParallel(todo_list,A_vals,B_vals,C_dict): 
    """
    take in some data that is used in the functions that will
    run in parallel.
    """

    total_list = []

    #make a function that will be run in parallel
    def computation(itemToCheck):
        checkedItems = checkVals(itemToCheck,A_vals,B_vals)
        results = []
        for item in checkedItems: 
                results.append(item)
        return results

    #in a loop keep sending something out for calculation in parallel until
    # all the combinations are done
    while len(todo_list) != 0:
            #use AsParallel on a list of items
            results = todo_list.AsParallel().SelectMany(
                            lambda itemToCheck: 
                                computation(itemToCheck) ).ToList()

            todo_list = []
            for item in results:
                if item not in total_list: 
                    total_list.append(item)

                    #do some modification to the dictionary that was passed in
                    C_dict[item] = None

    return total_list


def main():
    todo_list = [3,3,2,4,5,4,1,3,4,5,1]
    A_vals = [0,1,2,3,4,5,6]
    B_vals = [-1,-3,-5,-7,-9]
    C_dict = {0:-3,4:-7}

    newVals = CalcParallel(todo_list,A_vals,B_vals,C_dict)

    print(newVals)

main()

1 个答案:

答案 0 :(得分:1)

PLINQ的行为有点类似于TPL。使用相对轻量级的结构对作品进行分区/计划/调度。没有额外的ironpython进程,并且工作很可能安排在工作线程池中(通过任务),这意味着开销应该很小。

您正在使用的所有内容都被作用域/引用捕获,并且应该避免使用线程安全的共享集合,或者最好通过对数据流进行建模以使结果可以最终合并的方式来避免冲突的写操作(就像您正在使用SelectMany一样)。这应该不会导致您的数据副本。

为了确保高效执行,工作块的大小必须合理,以避免意外的开销。