【发布时间】:2016-09-22 14:21:28
【问题描述】:
阿霍伊。我的任务是提高Bit.ly's Data_Hacks'sample.py 的性能,作为练习。
我已经对部分代码进行了cythonized。并包含一个 PCG 随机生成器,它迄今已将性能提高了约 20 秒(从 72 秒下降),并优化了打印输出(通过使用基本的 c 函数,而不是 python 的write())。
这一切都很好,但除了这些修复之外,我想优化循环本身。
基本功能,见bit.ly的sample.py:
def run(sample_rate):
input_stream = sys.stdin
for line in input_stream:
if random.randint(1,100) <= sample_rate:
sys.stdout.write(line)
我的实现:
cdef int take_sample(float sample_rate):
cdef unsigned int floor = 1
cdef unsigned int top = 100
if pcg32_random() % 100 <= sample_rate:
return 1
else:
return 0
def run(float sample_rate, file):
cdef char* line
with open(file, 'rb') as f:
for line in f:
if take_sample(sample_rate):
out(line)
我现在想改进的是,如果我的 take_sample() 没有返回 True,则专门跳过下一行(最好重复这样做)。
我目前的实现是这样的:
def run(float sample_rate, file):
cdef char* line
with open(file, 'rb') as f:
for line in f:
out(line)
while not take_sample(sample_rate):
next(f)
这似乎对提高性能没有任何帮助 - 让我怀疑我只是在循环顶部的 if 条件之后用我的 next(f) 替换了 continue 调用。
所以问题是这样的:
是否有更有效的方法来循环文件(在 Cython 中)?
我想完全省略行,这意味着只有在我调用 out() 时才能真正访问它们 - 在 python 的 for 循环中是否已经出现这种情况?
line 是指向文件行的指针(或与之相当的指针)吗?还是循环实际上加载了这个?
我意识到我可以通过完全用 C 语言编写来改进它,但我想知道我可以在多大程度上继续使用 python/cython。
更新: 我已经测试了我的代码的 C 变体 - 使用相同的测试用例 - 它的时钟速度低于 2 秒(没有人感到惊讶)。因此,虽然随机生成器和文件 I/O 确实是总体上的两个主要瓶颈,但应该指出的是,python 的文件处理本身已经非常缓慢。
那么,除了将循环本身实现到 cython 之外,有没有办法利用 C 的文件读取?开销仍在显着降低 python 代码的速度,这让我想知道在使用 Cython 处理文件时我是否只是在性能的音墙上?
【问题讨论】:
-
这篇文章可能会有所帮助:rabexc.org/posts/io-performance-in-python
-
瓶颈是文件IO和随机函数。所以原始脚本没有实质性的优化潜力。
-
f上的每次迭代都会读取一行。一行被定义为直到下一个 '\nl' 的字节串。读者发现这一点的唯一方法是逐个读取字节。对于文本,没有可用于seek的行首索引。所以它不能不读当前就跳到下一行。 -
pcg32_random() % 100 <= sample_rate看起来不等于random.randint(1,100) <= sample_rate。它应该是pcg32_random() % 100 < sample_rate或pcg32_random() % 101 <= sample_rate,具体取决于pcg32_random()是否返回0。 -
一般情况下,将
%与随机数一起使用也是一个坏主意,我刚刚发现。不过感谢您的指点!
标签: python performance loops python-3.x cython