【发布时间】:2012-04-17 08:50:37
【问题描述】:
我在 Python 中实现了一个简单的合并排序算法。算法和测试代码如下:
import time
import random
import matplotlib.pyplot as plt
import math
from collections import deque
def sort(unsorted):
if len(unsorted) <= 1:
return unsorted
to_merge = deque(deque([elem]) for elem in unsorted)
while len(to_merge) > 1:
left = to_merge.popleft()
right = to_merge.popleft()
to_merge.append(merge(left, right))
return to_merge.pop()
def merge(left, right):
result = deque()
while left or right:
if left and right:
elem = left.popleft() if left[0] > right[0] else right.popleft()
elif not left and right:
elem = right.popleft()
elif not right and left:
elem = left.popleft()
result.append(elem)
return result
LOOP_COUNT = 100
START_N = 1
END_N = 1000
def test(fun, test_data):
start = time.clock()
for _ in xrange(LOOP_COUNT):
fun(test_data)
return time.clock() - start
def run_test():
timings, elem_nums = [], []
test_data = random.sample(xrange(100000), END_N)
for i in xrange(START_N, END_N):
loop_test_data = test_data[:i]
elapsed = test(sort, loop_test_data)
timings.append(elapsed)
elem_nums.append(len(loop_test_data))
print "%f s --- %d elems" % (elapsed, len(loop_test_data))
plt.plot(elem_nums, timings)
plt.show()
run_test()
据我所知,一切都很好,因此我应该得到一个漂亮的 N*logN 曲线。但是图片有点不同:
我试图调查该问题的事情:
- PyPy。曲线没问题。
- 使用 gc 模块禁用了 GC。猜错了。调试输出显示它甚至直到测试结束才运行。
- 使用 meliae 进行内存分析 - 没有什么特别或可疑的。 ` 我有另一种实现(使用相同合并函数的递归实现),它的行为方式类似。我创建的完整测试周期越多 - 曲线中的“跳跃”就越多。
那么如何解释并 - 希望 - 修复这种行为?
UPD:将列表更改为 collections.deque
UPD2:添加了完整的测试代码
UPD3:我在 Ubuntu 11.04 操作系统上使用 Python 2.7.1,使用四核 2Hz 笔记本。我试图关闭所有其他进程中的大多数:峰值的数量减少了,但至少其中一个仍然存在。
【问题讨论】:
-
您在列表中使用
.pop(0)。虽然我不确定这是否是这个特定运行时问题的原因,但它非常次优:列表在 CPython 中被实现为数组,如果你删除第一个元素,整个事情必须转移(这是一个O(n)操作)。您应该从最后弹出或使用链接列表,如collections.deque -
您正在查看极少数的元素。要获得对渐近运行时间的有用估计,您需要更大的数字。
-
@vkazanov:“list”是描述某些类型集合的通用术语:数组、单链表、双链表……但你说得对,Python 列表不是链表,可以假设。
-
你能否设置 python 进程的 CPU 亲和性(将其提取到单核),例如
taskset 1 python yourscript.py。这不应该是必要的,但以防万一。 -
@J.F.Sebastian 我已经设置了关联掩码,使用相同的数据多次运行代码(没有酸洗 - 在同一个过程中)。尖峰一直存在,在不同的地方。
标签: python sorting merge garbage-collection