Ray比Python和.multiprocessing都慢得多

Ray比Python和.multiprocessing都慢得多,python,ray,Python,Ray,我上传了130k个json文件 我使用Python: import os import json import pandas as pd path = "/my_path/" filename_ending = '.json' json_list = [] json_files = [file for file in os.listdir(f"{path}") if file.endswith(filename_ending)] import time start = time.ti

我上传了130k个json文件

我使用
Python

import os
import json
import pandas as pd

path = "/my_path/"

filename_ending = '.json'


json_list = []

json_files = [file for file in os.listdir(f"{path}") if file.endswith(filename_ending)]

import time
start = time.time()

for jf in json_files:
    with open(f"{path}/{jf}", 'r') as f:

        json_data = json.load(f)

        json_list.append(json_data)

end = time.time()
这需要60秒

我通过
多处理来实现这一点:

import os
import json
import pandas as pd
from multiprocessing import Pool
import time

path = "/my_path/"

filename_ending = '.json'

json_files = [file for file in os.listdir(f"{path}") if file.endswith(filename_ending)]


def read_data(name):
    with open(f"/my_path/{name}", 'r') as f:
        json_data = json.load(f)

    return json_data


if __name__ == '__main__':

    start = time.time()

    pool = Pool(processes=os.cpu_count())                       
    x = pool.map(read_data, json_files)     

    end = time.time()
这需要53秒

我使用
ray

import os
import json
import pandas as pd
from multiprocessing import Pool
import time
import ray


path = "/my_path/"

filename_ending = '.json'

json_files = [file for file in os.listdir(f"{path}") if file.endswith(filename_ending)]

start = time.time()

ray.shutdown()
ray.init(num_cpus=os.cpu_count()-1)

@ray.remote    
def read_data(name):
    with open(f"/my_path/{name}", 'r') as f:
        json_data = json.load(f)

    return json_data

all_data = []
for jf in json_files:
    all_data.append(read_data.remote(jf))


final = ray.get(all_data)

end = time.time()
这需要146秒

我的问题是为什么ray花了这么多时间

是否因为:

1) 对于相对较小的数据量,光线速度相对较慢

2) 我的代码有问题吗


3)
ray
没有那么有用吗?

我从未使用过ray,但我很有信心,我的解释应该是正确的

原始代码执行简单的json反序列化。该代码主要需要文件IO和少量CPU。(json反序列化相当快,这就是为什么json是一种流行的交换格式的原因之一)

Ray必须将数据从一个进程推送到另一个进程(如果通过网络分布在多台机器上)。为此,它自己执行一些序列化/反序列化(可能是使用pickle和健壮的TCP协议来推送参数和收集结果)。而且这个开销可能比实际任务所需的工作量更大

如果您对json数据进行更多的计算(任何CPU更密集的数据),那么您将能够看到不同之处

我的猜测是,您的示例问题太简单,因此ray的开销超过了使用多个worker的好处


换句话说。分配任务和收集结果所花费的时间/精力比计算结果实际花费的时间/精力要多。

我认为假设1)可能最接近事实。Ray似乎是一个功能强大的库,但你所做的只是读取一堆文件。您的代码只是为了基准测试的一个示例,还是某个更大程序的一部分?如果是后者,那么让您的基准代码反映这一点可能会很有趣

这没什么大不了的,但我调整了你的3个程序,所以它们至少应该稍微有点效率


导入操作系统
导入json
folder_path=“/my_path/”
filename_ending='.json'
json_files=(os.path.join(folder_path,fp)用于os.listdir(f“{folder_path}”)中的fp,如果fp.endswith(filename_ending))
def从_文件加载_json_(文件路径):
打开(文件路径“r”)作为文件“1”:
返回json.load(文件1)
json_list=[从_文件(curr_fp)为json_文件中的curr_fp加载_json_]

导入操作系统
导入json
将多处理作为mp导入
folder_path=“/my_path/”
filename_ending='.json'
json_files=(os.path.join(folder_path,fp)用于os.listdir(f“{folder_path}”)中的fp,如果fp.endswith(filename_ending))
def从_文件加载_json_(文件路径):
打开(文件路径“r”)作为文件“1”:
返回json.load(文件1)
使用mp.Pool()作为池:
json_list=pool.map(从_文件、json_文件加载_json_)

导入操作系统
导入json
导入光线
folder_path=“/my_path/”
filename_ending='.json'
@雷,遥控器
def从_文件加载_json_(文件路径):
打开(文件路径“r”)作为文件“1”:
返回json.load(文件1)
json_files=(os.path.join(folder_path,fp)用于os.listdir(f“{folder_path}”)中的fp,如果fp.endswith(filename_ending))
ray.init()
futures_list=[从_file.remote(curr_fp)为json_文件中的curr_fp加载_json_]
json_list=ray.get(futures_list)


如果你有任何问题,请告诉我。如果你能再次运行基准测试,我很想知道有什么不同,如果有的话。

@RobertNishihara,有什么想法吗?Ray是分布式计算库,对吗?@AlexanderCécile,是的:)好的,我目前正在做一些快速的研究,并试图写出一个答案:)你能分享一个JSON文件进行测试吗?你怎么知道Ray正在做任何与网络相关的事情?我不知道,如果它在一台主机上运行,它是否真的在联网,如果它使用集群的话。对于单主机设置,它可能只是本地TCP套接字、unix域套接字、本地管道或共享内存。但在大多数情况下,串行化、反串行化、交换数据和同步都会有相当大的开销。我修改了我的回答我很确定Ray使用Redis服务器来处理进程间的通信,即使是本地的,所以启动时可能会受到不可忽视的损失。再加上这里绝大多数工作都是文件系统I/O,我很有信心@gelonida是正确的。嘿,谢谢你的回复(upvote)。我同意假设(1)在我的案例中更为正确,尽管有证据证明,
Ray
在数据较大的情况下实际上更好。顺便问一下,关于你的第二段代码,你为什么不使用
\uuuu main\uuuu
?我认为(根据它的文档)要使用
多处理
就必须使用
\uuuuu main\uuuu
?@PoeteMaudit Oops,我忘记了这个评论,对不起!在Windows上使用
多处理
时,
\uuuuuu main\uuuuuu
似乎是绝对必要的。我用的是Mac电脑,但对我来说编写
\uuuu main\uuuu
部分可能是个好主意。请参阅,以了解为什么它在Windows上是必须的,以及关于这个主题的更一般性的讨论。很酷,但我也使用它运行了
多处理
:使用mp.Pool(processs=os.cpu\u count()-1)作为Pool:output=Pool.map(my\u函数,input)
那么没有
\uuuuu main\uuuu
@PoeteMaudit,你就不用Windows了吗?啊,是的,这是一个很好的观点。我在远程服务器上运行它,所以我不知道它是什么。我想是Linux吧?