python 프로그래밍 pdf - C ++에서 stdin에서 읽는 줄이 파이썬보다 느린 이유는 무엇입니까?





5 Answers

그냥 호기심에서 나는 두포에서 일어나는 일을 살펴 dtruss/strace 각 테스트마다 dtruss/strace 를 사용 dtruss/strace .

C ++

./a.out < in
Saw 6512403 lines in 8 seconds.  Crunch speed: 814050

syscalls sudo dtruss -c ./a.out < in

CALL                                        COUNT
__mac_syscall                                   1
<snip>
open                                            6
pread                                           8
mprotect                                       17
mmap                                           22
stat64                                         30
read_nocancel                               25958

파이썬

./a.py < in
Read 6512402 lines in 1 seconds. LPS: 6512402

syscalls sudo dtruss -c ./a.py < in

CALL                                        COUNT
__mac_syscall                                   1
<snip>
open                                            5
pread                                           8
mprotect                                       17
mmap                                           21
stat64                                         29
나의 실전편 고성능

나는 파이썬과 C ++을 사용하는 stdin에서 문자열 입력을 읽는 것을 비교하기를 원했고 나의 C ++ 코드가 동등한 파이썬 코드보다 느린 순서로 실행되는 것을보고 놀랐다. 내 C ++이 녹슬었고 아직 Pythonista 전문가가 아니기 때문에 내가 잘못한 일을하고 있는지, 아니면 내가 오해하고 있는지 말해줘.

(TLDR 대답 : 성명을 포함 : cin.sync_with_stdio(false) 아니면 단지 대신 fgets 사용합니다.

TLDR 결과 : 맨 아래까지 스크롤하여 표를 봅니다.)

C ++ 코드 :

#include <iostream>
#include <time.h>

using namespace std;

int main() {
    string input_line;
    long line_count = 0;
    time_t start = time(NULL);
    int sec;
    int lps;

    while (cin) {
        getline(cin, input_line);
        if (!cin.eof())
            line_count++;
    };

    sec = (int) time(NULL) - start;
    cerr << "Read " << line_count << " lines in " << sec << " seconds.";
    if (sec > 0) {
        lps = line_count / sec;
        cerr << " LPS: " << lps << endl;
    } else
        cerr << endl;
    return 0;
}

// Compiled with:
// g++ -O3 -o readline_test_cpp foo.cpp

동등한 파이썬 :

#!/usr/bin/env python
import time
import sys

count = 0
start = time.time()

for line in  sys.stdin:
    count += 1

delta_sec = int(time.time() - start_time)
if delta_sec >= 0:
    lines_per_sec = int(round(count/delta_sec))
    print("Read {0} lines in {1} seconds. LPS: {2}".format(count, delta_sec,
       lines_per_sec))

내 결과는 다음과 같습니다.

$ cat test_lines | ./readline_test_cpp
Read 5570000 lines in 9 seconds. LPS: 618889

$cat test_lines | ./readline_test.py
Read 5570000 lines in 1 seconds. LPS: 5570000

Mac OS X v10.6.8 (Snow Leopard) 및 Linux 2.6.32 (Red Hat Linux 6.2)에서이 작업을 시도했음을 유의해야합니다. 전자는 MacBook Pro이고, 후자는 매우 가벼운 서버입니다. 너무 적절하지는 않습니다.

$ for i in {1..5}; do echo "Test run $i at `date`"; echo -n "CPP:"; cat test_lines | ./readline_test_cpp ; echo -n "Python:"; cat test_lines | ./readline_test.py ; done
Test run 1 at Mon Feb 20 21:29:28 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 2 at Mon Feb 20 21:29:39 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 3 at Mon Feb 20 21:29:50 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 4 at Mon Feb 20 21:30:01 EST 2012
CPP:   Read 5570001 lines in 9 seconds. LPS: 618889
Python:Read 5570000 lines in 1 seconds. LPS: 5570000
Test run 5 at Mon Feb 20 21:30:11 EST 2012
CPP:   Read 5570001 lines in 10 seconds. LPS: 557000
Python:Read 5570000 lines in  1 seconds. LPS: 5570000

작은 기준 부록 및 요점

완전성을 위해, 나는 원래의 (동기화 된) C ++ 코드와 동일한 상자에 같은 파일에 대한 읽기 속도를 업데이트 할 것이라고 생각했습니다. 다시 말하지만, 이것은 빠른 디스크의 100M 라인 파일에 대한 것입니다. 몇 가지 솔루션 / 접근법을 비교해 보겠습니다.

Implementation      Lines per second
python (default)           3,571,428
cin (default/naive)          819,672
cin (no sync)             12,500,000
fgets                     14,285,714
wc (not fair comparison)  54,644,808



Mac에서 g ++를 사용하여 컴퓨터에서 원래 결과를 재현했습니다.

while 문 바로 전에 C ++ 버전에 다음 문을 추가하면 Python 버전으로 인라인됩니다.

std::ios_base::sync_with_stdio(false);
char buffer[1048576];
std::cin.rdbuf()->pubsetbuf(buffer, sizeof(buffer));

sync_with_stdio는 속도를 2 초로 향상 시켰으며, 큰 버퍼를 설정하면 1 초가되었습니다.




그런데 C ++ 버전의 줄수가 파이썬 버전의 개수보다 큰 이유는 eof 플래그가 eof 이상으로 읽으려는 시도가있을 때만 설정된다는 것입니다. 올바른 루프는 다음과 같습니다.

while (cin) {
    getline(cin, input_line);

    if (!cin.eof())
        line_count++;
};



다음 코드는 지금까지 게시 된 다른 코드보다 훨씬 빠릅니다. (Visual Studio 2013, 64 비트, 500MB 파일, 줄 길이는 [0, 1000]로 균등합니다).

const int buffer_size = 500 * 1024;  // Too large/small buffer is not good.
std::vector<char> buffer(buffer_size);
int size;
while ((size = fread(buffer.data(), sizeof(char), buffer_size, stdin)) > 0) {
    line_count += count_if(buffer.begin(), buffer.begin() + size, [](char ch) { return ch == '\n'; });
}

그것은 나의 모든 파이썬 시도를 두 번째 요소 이상으로 이깁니다.




글쎄, 당신의 두 번째 솔루션에서 당신이 cinf에서 scanf로 바뀌 었음을 알았습니다. 이것이 내가 당신을 만들려고했던 첫 번째 제안이었습니다 (cin은 sloooooooooooow입니다). 이제 scanf 에서 fgets 전환하면 퍼포먼스가 또 한번 향상됩니다. fgets 는 문자열 입력을위한 가장 빠른 C ++ 함수입니다.

BTW, 동기화 문제에 대해 잘 알지 못했습니다. 그러나 당신은 fgets 시도해야합니다.




Related