迭代基于条件有效地省略行的文件

时间:2016-05-25 06:28:49

标签: python performance loops python-3.x cython

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进行文件处理时?

1 个答案:

答案 0 :(得分:0)

如果文件很小,您可以立即用.readlines()读取它(可能减少IO流量)并迭代行序列。 如果采样率足够小,您可以考虑从几何分布中抽样,这可能更有效。

我不知道cython,但我也会考虑:

  • 通过删除不必要的变量来简化take_sample()并返回测试的布尔结果而不是整数,
  • take_sample()的签名更改为take_sample(int),以避免每次测试都进行int-to-float转换。

[编辑]

根据@hpaulj的评论,如果您使用.read().split('\n')而不是我建议的.readlines(),可能会更好。