如何诊断两台机器之间 Python 脚本(pandas 聚合)运行时的巨大差异?
How do I diagnose an egregious difference in Python script (pandas aggregate) runtime between two machines?
我在两台机器上使用相同的 Python 脚本 运行ning,输入相同,输出相同。在我的笔记本电脑上 运行 大约需要 7 分钟,在我上传到的服务器上 运行 需要 56 分钟。
显然,环境不同,但速度差异 (8x!) 对我来说有点太过分了,不能把它写成不同的环境。两台机器都有足够的内存,而且服务器的内核比我的笔记本电脑多,但上面 运行 的东西也更多(而且我认为内核对于此操作并不重要)。我的笔记本电脑和服务器上 运行 都是正常的。我以前从未有过 2 倍的速度差异,更不用说 10 倍了。
该脚本非常样本,大部分工作是 pandas.DataFrame.groupby().aggregate(lambda)
,但在一个大的 (~400 MB) table.
如何诊断造成速度差异的原因?
以下是我迄今为止尝试过的内容,但我对此知之甚少,因此可能有趣也可能不有趣。
cProfile
我在两种环境中都尝试了 运行ning python -m cProfile
以查看是否有任何特定的函数调用花费了很长时间,但它似乎是全面的:
笔记本电脑:
ncalls tottime percall cumtime percall filename:lineno(function)
93 56.976 0.613 372.694 4.007 {method 'get_result' of 'pandas.lib.SeriesGrouper' objects}
16338970 46.629 0.000 250.104 0.000 aggregate_sv_calls_by_sv_id.py:42(<lambda>)
18442616 33.808 0.000 56.950 0.000 {sorted}
18442645 25.395 0.000 172.113 0.000 series.py:1033(__iter__)
78056747/78056745 15.405 0.000 15.420 0.000 {isinstance}
18446903 14.235 0.000 34.129 0.000 dtypes.py:68(is_dtype)
18443264 13.515 0.000 21.058 0.000 internals.py:3806(dtype)
18442666 13.447 0.000 30.854 0.000 common.py:2192(is_datetime_or_timedelta_dtype)
18449428 13.250 0.000 13.250 0.000 {hasattr}
18442793 13.001 0.000 19.134 0.000 internals.py:3833(internal_values)
...
服务器:
ncalls tottime percall cumtime percall filename:lineno(function)
312253983 175.564 0.000 275.203 0.000 {isinstance}
93 153.294 1.648 3323.806 35.740 groupby.py:1885(_aggregate_series_pure_python)
16338970 151.336 0.000 749.431 0.000 aggregate_sv_calls_by_sv_id.py:42(<lambda>)
18443129 146.950 0.000 607.143 0.000 internals.py:2482(make_block)
18442874 136.363 0.000 529.415 0.000 series.py:120(__init__)
18443241 125.725 0.000 248.772 0.000 generic.py:2674(__setattr__)
18442596 106.353 0.000 1336.629 0.000 internals.py:3868(get_slice)
18442617 106.296 0.000 172.363 0.000 {sorted}
18442596 89.203 0.000 2105.284 0.000 series.py:689(_get_values)
18443048 84.777 0.000 91.065 0.000 base.py:506(_get_attributes_dict)
...
因为它似乎是全面的,而不是我可以隔离的任何一个功能,我试图看看我是否可以将问题减少到 minimum, complete, verifiable example...这是我得到的最好的:
%timeit
笔记本电脑:
In [5]: df = pd.DataFrame(np.random.randint(10, size=(10000, 50)))
In [6]: %timeit df.groupby(0).sum()
100 loops, best of 3: 5.54 ms per loop
In [7]: %timeit df.groupby(0).agg(lambda x: sum(x))
1 loops, best of 3: 124 ms per loop
In [8]: %timeit df.groupby(0, 1).agg(lambda x: sum(x))
1 loops, best of 3: 155 ms per loop
服务器:
In [5]: df = pd.DataFrame(np.random.randint(10, size=(10000, 50)))
In [6]: %timeit df.groupby(0).sum()
100 loops, best of 3: 6.08 ms per loop
In [7]: %timeit df.groupby(0).agg(lambda x: sum(x))
1 loops, best of 3: 215 ms per loop
In [8]: %timeit df.groupby(0, 1).agg(lambda x: sum(x))
1 loops, best of 3: 327 ms per loop
所以这并没有完全捕捉到 8 倍的速度差异。它只显示大约 2 倍的速度差异。
但是这种差异 (2x) 我觉得我已经习惯了归因于某种我可能无法弄清楚的环境因素,比如服务器上的其他负载或类似的东西, 或不同的处理器。如果我愿意注销不同机器的 2 倍差异,是否意味着我也必须愿意接受不同机器的 8 倍差异?还是这里有更多有趣的东西可以挖掘?
结论:
我的 pandas
版本在我的笔记本电脑上是 0.17.1,在服务器上是 0.18.1。我在我的笔记本电脑上比较了 0.17.1 和 0.18.1 之间的相同代码 运行ning,而 0.17.1 运行 大约快 4 倍。所以 ~4x 可以归因于此,~2x 可以归因于笔记本电脑和服务器之间的平台差异。
我怀疑您正在寻找两个,甚至三个不同的原因。
通过比较两个配置文件,我看到:
相同的例程(已排序),标准分布的一部分,执行次数大致相同,显示出 3 倍的时间差异。此例程是 CPU 和内存绑定,没有其他需求,因此这可能表明平台差异(更快 CPU、更慢的内存、大量页面错误和可能的交换抖动)。在对 aggregate_sv_calls_by_sv_id.py
.
的调用中也可以看到同样的情况
该例程在服务器上少执行了一次,这表明与相同算法的运行不同。这可能与下一个元素相同,或者表示某种不确定性。
看似相同的例程,在笔记本电脑和服务器上执行了相同的重要次数 (93),此处称为 {method 'get_result' of 'pandas.lib.SeriesGrouper' objects}
,groupby.py:1885(_aggregate_series_pure_python)
那里。 这意味着软件基础存在差异。
总而言之,我敢说您的软件版本 本身 比服务器的快 2.5 倍左右。然后,服务器 运行 使脚本慢 3 倍。如果这是可重现的,则 2.5 * 3 给出了您观察到的大约 8X 因子。否则,要么真正的原因是三个而不是两个,并且上述不确定性起作用,要么服务器上的 3 倍减速是由于偶然情况(例如系统负载,很可能)。
我在两台机器上使用相同的 Python 脚本 运行ning,输入相同,输出相同。在我的笔记本电脑上 运行 大约需要 7 分钟,在我上传到的服务器上 运行 需要 56 分钟。
显然,环境不同,但速度差异 (8x!) 对我来说有点太过分了,不能把它写成不同的环境。两台机器都有足够的内存,而且服务器的内核比我的笔记本电脑多,但上面 运行 的东西也更多(而且我认为内核对于此操作并不重要)。我的笔记本电脑和服务器上 运行 都是正常的。我以前从未有过 2 倍的速度差异,更不用说 10 倍了。
该脚本非常样本,大部分工作是 pandas.DataFrame.groupby().aggregate(lambda)
,但在一个大的 (~400 MB) table.
如何诊断造成速度差异的原因?
以下是我迄今为止尝试过的内容,但我对此知之甚少,因此可能有趣也可能不有趣。
cProfile
我在两种环境中都尝试了 运行ning python -m cProfile
以查看是否有任何特定的函数调用花费了很长时间,但它似乎是全面的:
笔记本电脑:
ncalls tottime percall cumtime percall filename:lineno(function)
93 56.976 0.613 372.694 4.007 {method 'get_result' of 'pandas.lib.SeriesGrouper' objects}
16338970 46.629 0.000 250.104 0.000 aggregate_sv_calls_by_sv_id.py:42(<lambda>)
18442616 33.808 0.000 56.950 0.000 {sorted}
18442645 25.395 0.000 172.113 0.000 series.py:1033(__iter__)
78056747/78056745 15.405 0.000 15.420 0.000 {isinstance}
18446903 14.235 0.000 34.129 0.000 dtypes.py:68(is_dtype)
18443264 13.515 0.000 21.058 0.000 internals.py:3806(dtype)
18442666 13.447 0.000 30.854 0.000 common.py:2192(is_datetime_or_timedelta_dtype)
18449428 13.250 0.000 13.250 0.000 {hasattr}
18442793 13.001 0.000 19.134 0.000 internals.py:3833(internal_values)
...
服务器:
ncalls tottime percall cumtime percall filename:lineno(function)
312253983 175.564 0.000 275.203 0.000 {isinstance}
93 153.294 1.648 3323.806 35.740 groupby.py:1885(_aggregate_series_pure_python)
16338970 151.336 0.000 749.431 0.000 aggregate_sv_calls_by_sv_id.py:42(<lambda>)
18443129 146.950 0.000 607.143 0.000 internals.py:2482(make_block)
18442874 136.363 0.000 529.415 0.000 series.py:120(__init__)
18443241 125.725 0.000 248.772 0.000 generic.py:2674(__setattr__)
18442596 106.353 0.000 1336.629 0.000 internals.py:3868(get_slice)
18442617 106.296 0.000 172.363 0.000 {sorted}
18442596 89.203 0.000 2105.284 0.000 series.py:689(_get_values)
18443048 84.777 0.000 91.065 0.000 base.py:506(_get_attributes_dict)
...
因为它似乎是全面的,而不是我可以隔离的任何一个功能,我试图看看我是否可以将问题减少到 minimum, complete, verifiable example...这是我得到的最好的:
%timeit
笔记本电脑:
In [5]: df = pd.DataFrame(np.random.randint(10, size=(10000, 50)))
In [6]: %timeit df.groupby(0).sum()
100 loops, best of 3: 5.54 ms per loop
In [7]: %timeit df.groupby(0).agg(lambda x: sum(x))
1 loops, best of 3: 124 ms per loop
In [8]: %timeit df.groupby(0, 1).agg(lambda x: sum(x))
1 loops, best of 3: 155 ms per loop
服务器:
In [5]: df = pd.DataFrame(np.random.randint(10, size=(10000, 50)))
In [6]: %timeit df.groupby(0).sum()
100 loops, best of 3: 6.08 ms per loop
In [7]: %timeit df.groupby(0).agg(lambda x: sum(x))
1 loops, best of 3: 215 ms per loop
In [8]: %timeit df.groupby(0, 1).agg(lambda x: sum(x))
1 loops, best of 3: 327 ms per loop
所以这并没有完全捕捉到 8 倍的速度差异。它只显示大约 2 倍的速度差异。
但是这种差异 (2x) 我觉得我已经习惯了归因于某种我可能无法弄清楚的环境因素,比如服务器上的其他负载或类似的东西, 或不同的处理器。如果我愿意注销不同机器的 2 倍差异,是否意味着我也必须愿意接受不同机器的 8 倍差异?还是这里有更多有趣的东西可以挖掘?
结论:
我的 pandas
版本在我的笔记本电脑上是 0.17.1,在服务器上是 0.18.1。我在我的笔记本电脑上比较了 0.17.1 和 0.18.1 之间的相同代码 运行ning,而 0.17.1 运行 大约快 4 倍。所以 ~4x 可以归因于此,~2x 可以归因于笔记本电脑和服务器之间的平台差异。
我怀疑您正在寻找两个,甚至三个不同的原因。
通过比较两个配置文件,我看到:
相同的例程(已排序),标准分布的一部分,执行次数大致相同,显示出 3 倍的时间差异。此例程是 CPU 和内存绑定,没有其他需求,因此这可能表明平台差异(更快 CPU、更慢的内存、大量页面错误和可能的交换抖动)。在对
aggregate_sv_calls_by_sv_id.py
. 的调用中也可以看到同样的情况
该例程在服务器上少执行了一次,这表明与相同算法的运行不同。这可能与下一个元素相同,或者表示某种不确定性。
看似相同的例程,在笔记本电脑和服务器上执行了相同的重要次数 (93),此处称为
{method 'get_result' of 'pandas.lib.SeriesGrouper' objects}
,groupby.py:1885(_aggregate_series_pure_python)
那里。 这意味着软件基础存在差异。
总而言之,我敢说您的软件版本 本身 比服务器的快 2.5 倍左右。然后,服务器 运行 使脚本慢 3 倍。如果这是可重现的,则 2.5 * 3 给出了您观察到的大约 8X 因子。否则,要么真正的原因是三个而不是两个,并且上述不确定性起作用,要么服务器上的 3 倍减速是由于偶然情况(例如系统负载,很可能)。