【问题标题】:Iterating over a file omitting lines based on condition efficiently根据条件有效地迭代文件省略行
【发布时间】: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 &lt;= sample_rate 看起来不等于 random.randint(1,100) &lt;= sample_rate。它应该是pcg32_random() % 100 &lt; sample_ratepcg32_random() % 101 &lt;= sample_rate,具体取决于pcg32_random() 是否返回0。
  • 一般情况下,将% 与随机数一起使用也是一个坏主意,我刚刚发现。不过感谢您的指点!

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


【解决方案1】:

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

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

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

[编辑]

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2019-06-30
  • 2016-03-12
  • 1970-01-01
  • 2019-01-28
  • 2022-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多