AHOI。我的任务是提高Bit.ly's Data_Hacks' sample.py的性能,作为练习练习。
我已经对部分代码进行了cython化。并包括一个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)
这似乎无助于提高性能 - 让我怀疑我只是在我的continue
循环顶部的if条件之后替换了next(f)
调用。
所以问题是:
是否有更有效的方法循环文件(在Cython中)?
我想完全省略行,这意味着只有在我调用out()
时才能真正访问它们 - 这是python的for
循环中的情况吗?
line
是指向文件行的指针(或与此类似)吗?或者循环实际加载了吗?
我意识到我可以通过完全用C语言编写它来改进它,但我想知道我可以用python / cython推进这个有多远。
更新: 我已经测试了我的代码的C变体 - 使用相同的测试用例 - 它的时钟频率低于2秒(令人惊讶的是没有人)。因此,虽然随机生成器和文件I / O通常是两个主要瓶颈,但应该指出python的文件处理本身已经很慢了。
那么,有没有办法利用C的文件读取,而不是将循环本身实现到cython?开销仍然显着降低了python代码的速度,这让我想知道我是否只是在性能的声音墙上,当谈到使用Cython进行文件处理时?
答案 0 :(得分:0)
如果文件很小,您可以立即用.readlines()
读取它(可能减少IO流量)并迭代行序列。
如果采样率足够小,您可以考虑从几何分布中抽样,这可能更有效。
我不知道cython,但我也会考虑:
take_sample()
并返回测试的布尔结果而不是整数,take_sample()
的签名更改为take_sample(int)
,以避免每次测试都进行int-to-float转换。[编辑]
根据@hpaulj的评论,如果您使用.read().split('\n')
而不是我建议的.readlines()
,可能会更好。