为什么Python3在我的任务上比Python2慢得多?

时间:2017-11-07 12:30:27

标签: python python-2.7 python-3.x performance

我很惊讶地知道Python 3.5.2Python 2.7.12慢得多。我写了一个简单的命令行命令来计算巨大的CSV文件中的行数。

$ cat huge.csv | python -c "import sys; print(sum(1 for _ in sys.stdin))"
101253515
# it took 15 seconds

$ cat huge.csv | python3 -c "import sys; print(sum(1 for _ in sys.stdin))"
101253515
# it took 66 seconds
Python 2.7.12耗时15秒,Python 3.5.2耗时66秒。我预计差异可能会发生,但为什么会如此巨大呢? Python 3中有哪些新功能使得这类任务变得更慢?有没有更快的方法来计算Python 3中的行数?

我的CPU是Intel(R) Core(TM) i5-3570 CPU @ 3.40GHz

huge.csv的大小为18.1 Gb,包含101253515行。

提出这个问题,我不需要不惜一切代价找到大文件的行数。我刚刚编写了一个特殊情况,其中Python 3要慢得多。实际上,我正在使用Python 3开发一个处理大型CSV文件的脚本,有些操作不会假设使用csv库。我知道,我可以用Python 2编写脚本,这对速度来说是可以接受的。但是我想知道在Python 3中编写类似脚本的方法。这就是为什么我感兴趣的是在我的示例中使Python 3变慢的原因以及如何通过“诚实”的python方法来改进它。

1 个答案:

答案 0 :(得分:5)

sys.stdin对象在Python3中有点复杂,然后在Python2中。例如,默认情况下,在Python3中从sys.stdin读取将输入转换为unicode,因此在非unicode字节上失败:

$ echo -e "\xf8" | python3 -c "import sys; print(sum(1 for _ in sys.stdin))"

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "<string>", line 1, in <genexpr>
  File "/usr/lib/python3.5/codecs.py", line 321, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf8 in position 0: invalid start byte

请注意,Python2对该输入没有任何问题。所以你可以看到Python3&#39; s sys.stdin做了更多的事情。我不确定这是否对性能损失负有责任,但您可以通过在Python3下尝试sys.stdin.buffer来进一步调查:

import sys
print(sum(1 for _ in sys.stdin.buffer))

请注意,{2}在Python2中不存在.buffer。我已经完成了一些测试,但我并没有发现Python2的sys.stdin和Python3的sys.stdin.buffer之间的性能存在真正的差异,而是YMMV。

编辑以下是我机器上的一些随机结果:ubuntu 16.04,i7 cpu,8GiB RAM。首先是一些C代码(作为比较的基础):

#include <unistd.h>

int main() {
    char buffer[4096];
    size_t total = 0;
    while (true) {
        int result = ::read(STDIN_FILENO, buffer, sizeof(buffer));
        total += result;
        if (result == 0) {
            break;
        }
    }
    return 0;
};

现在文件大小:

$ ls -s --block-size=M | grep huge2.txt 
10898M huge2.txt

和测试:

// a.out is a simple C equivalent code (except for the final print)
$ time cat huge2.txt | ./a.out

real    0m20.607s
user    0m0.236s
sys     0m10.600s


$ time cat huge2.txt | python -c "import sys; print(sum(1 for _ in sys.stdin))"
898773889

real    1m24.268s
user    1m20.216s
sys     0m8.724s


$ time cat huge2.txt | python3 -c "import sys; print(sum(1 for _ in sys.stdin.buffer))"
898773889

real    1m19.734s
user    1m14.432s
sys     0m11.940s


$ time cat huge2.txt | python3 -c "import sys; print(sum(1 for _ in sys.stdin))"
898773889

real    2m0.326s
user    1m56.148s
sys     0m9.876s

所以我使用的文件有点小,时间也更长(看起来你有更好的机器而我没有耐心等待更大的文件:D)。无论如何,Python2和Python3的sys.stdin.buffer在我的测试中非常相似。 Python3的sys.stdin速度慢了。所有这些都是C代码背后的代码(用户时间几乎为0)。