Ray 比 Python 和 .multiprocessing 慢得多

Posted

技术标签:

【中文标题】Ray 比 Python 和 .multiprocessing 慢得多【英文标题】:Ray is much slower both than Python and .multiprocessing 【发布时间】:2020-03-01 07:18:37 【问题描述】:

我上传了 13 万个 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 秒。

我用multiprocessing 做这个:

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) 对于相对少量的数据,ray 相对较慢?

2) 我的代码做错了什么?

3) ray 不是很有用吗?

【问题讨论】:

@RobertNishihara,有什么想法吗? Ray 是分布式计算的库,对吧? @AlexanderCécile,是的 :) 好吧,我目前正在做一些快速研究并试图写一个答案:) 您能分享一下用于测试的 JSON 文件吗? 【参考方案1】:

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

原始代码做了一个简单的 json 反序列化。该代码主要需要文件 IO 和一点点 CPU。 (json反序列化比较快,这也是json成为流行交换格式的原因之一)

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

如果您对 json 数据进行更多计算(任何更占用 CPU 的数据),那么您将能够看到差异。

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

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

【讨论】:

你怎么知道 Ray 正在做任何与网络相关的事情? 我不知道,如果在单个主机上运行它是否真的是网络,如果使用集群它会。对于单主机设置,它可能只是本地 TCP 套接字、unix 域套接字、本地管道或共享内存。但在大多数情况下,序列化、反序列化、交换数据和同步都会产生相当大的开销。我调整了我的答案 我很确定 Ray 使用 Redis 服务器来处理进程间通信,甚至是本地通信,因此可能存在不可忽略的启动损失。结合这里的绝大多数工作是文件系统 I/O 的事实,我非常有信心 @gelonida 是正确的。【参考方案2】:

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

这没什么大不了的,但我调整了你的 3 个程序,所以它们至少应该更高效一些。


import os
import json


folder_path = "/my_path/"
filename_ending = '.json'

json_files = (os.path.join(folder_path, fp) for fp in os.listdir(f"folder_path") if fp.endswith(filename_ending))


def load_json_from_file(file_path):
    with open(file_path, 'r') as file_1:
        return json.load(file_1)


json_list = [load_json_from_file(curr_fp) for curr_fp in json_files]

import os
import json
import multiprocessing as mp


folder_path = "/my_path/"
filename_ending = '.json'

json_files = (os.path.join(folder_path, fp) for fp in os.listdir(f"folder_path") if fp.endswith(filename_ending))


def load_json_from_file(file_path):
    with open(file_path, 'r') as file_1:
        return json.load(file_1)


with mp.Pool() as pool:       
    json_list = pool.map(load_json_from_file, json_files)  

import os
import json
import ray

folder_path = "/my_path/"
filename_ending = '.json'


@ray.remote
def load_json_from_file(file_path):
    with open(file_path, 'r') as file_1:
        return json.load(file_1)


json_files = (os.path.join(folder_path, fp) for fp in os.listdir(f"folder_path") if fp.endswith(filename_ending))

ray.init()

futures_list = [load_json_from_file.remote(curr_fp) for curr_fp in json_files]

json_list = ray.get(futures_list)

如果您有任何问题,请告诉我。如果您可以再次运行基准测试,我很想知道有什么区别(如果有的话)。

【讨论】:

您好,感谢您的回复(点赞)。我同意假设(1)在我的情况下可能更正确,尽管有待证明Ray实际上在更大的数据下更好。顺便说一句,关于你的第二个代码块,你怎么不使用__main__?我认为(基于其文档)要使用multiprocessing,您必须使用__main__ @PoeteMaudit 哎呀,我忘记了这条评论,对不起!在 Windows 上使用 multiprocessing 时,__main__ 似乎是绝对必要的。我使用的是 Mac,但无论如何写 __main__ 部分对我来说可能是个好主意。请参阅here 了解为什么它在 Windows 上是必须的,here 了解有关该主题的更一般性讨论。 很酷,但我也让multiprocessing 运行:with mp.Pool(processes=os.cpu_count()-1) as pool: output = pool.map(my_function, input) 所以没有__main__ @PoeteMaudit 你没有使用 Windows? 啊是的好点。我在远程服务器上运行它,所以我不知道它是什么。我想是 Linux 吧?

以上是关于Ray 比 Python 和 .multiprocessing 慢得多的主要内容,如果未能解决你的问题,请参考以下文章

使用ray进行最近邻搜索的并行化

kali安装机场v2ray

等到任务用 Python 中的 Ray 完成

如何在 Ray 中使用 python 日志记录?

如何使用 python Ray 在一个大列表上并行化?

在 Python 中使用 Ray 并行化任务,得到“Aborted (core dumped)”