为什么使用大熊猫的多处理应用会带来如此惊人的加速?

Adr*_*ian 5 python performance multiprocessing dataframe pandas

假设我有一个pandas数据帧和一个我想要应用于每一行的函数.我可以打电话df.apply(apply_fn, axis=1),这应该花费时间线性的大小df.或者我可以拆分df并使用pool.map在每个部分上调用我的函数,然后连接结果.

我期望使用的加速因子pool.map大致等于池中的进程数(new_execution_time = original_execution_time/N,如果使用N个处理器 - 并且假设零开销).

相反,在这个玩具示例中,使用4个处理器时,时间下降到2%左右(0.005272/0.230757).我最多只期望25%.发生了什么事,我不理解什么?

import numpy as np
from multiprocessing import Pool
import pandas as pd
import pdb
import time

n = 1000
variables = {"hello":np.arange(n), "there":np.random.randn(n)}
df = pd.DataFrame(variables)

def apply_fn(series):
    return pd.Series({"col_5":5, "col_88":88,
                      "sum_hello_there":series["hello"] + series["there"]})

def call_apply_fn(df):
    return df.apply(apply_fn, axis=1)

n_processes = 4  # My machine has 4 CPUs
pool = Pool(processes=n_processes)

t0 = time.process_time()
new_df = df.apply(apply_fn, axis=1)
t1 = time.process_time()
df_split = np.array_split(df, n_processes)
pool_results = pool.map(call_apply_fn, df_split)
new_df2 = pd.concat(pool_results)
t2 = time.process_time()
new_df3 = df.apply(apply_fn, axis=1)  # Try df.apply a second time
t3 = time.process_time()

print("identical results: %s" % np.all(np.isclose(new_df, new_df2)))  # True
print("t1 - t0 = %f" % (t1 - t0))  # I got 0.230757
print("t2 - t1 = %f" % (t2 - t1))  # I got 0.005272
print("t3 - t2 = %f" % (t3 - t2))  # I got 0.229413
Run Code Online (Sandbox Code Playgroud)

我保存上面的代码并使用它运行它python3 my_filename.py.

PS我意识到在这个玩具示例中new_df可以以更直接的方式创建,而无需使用apply.我有兴趣将类似的代码应用于更复杂的代码,apply_fn而不仅仅是添加列.

ptr*_*trj 1

编辑(我之前的答案实际上是错误的。)

time.process_time()( doc ) 仅测量当前进程的时间(不包括睡眠时间)。所以不考虑子进程花费的时间。

我用 运行你的代码time.time(),它测量真实世界的时间(根本没有显示加速)并且更可靠timeit.timeit(大约 50% 的加速)。我有4核。