저는 약간의 속도를 얻고 내 녹슨 C ++ 기술을 연마하기 위해 일부 코드를 Python에서 C ++로 변환하려고합니다. 표준 입력에서 라인을 읽는 순진 구현 (참조 ++ 훨씬 빠르게 C보다는 파이썬 때 어제 나는 충격을 받았다 이 ). 오늘, 마침내 분리 문자 병합 (파이썬의 split ()과 유사한 의미)을 사용하여 C ++에서 문자열을 분할하는 방법을 알아 냈고 이제 deja vu를 경험하고 있습니다! 내 C ++ 코드는 작업을 수행하는 데 훨씬 더 오래 걸립니다 (어제 수업의 경우와 같이 훨씬 더 많은 것은 아니지만).
Python 코드 :
#!/usr/bin/env python
from __future__ import print_function
import time
import sys
count = 0
start_time = time.time()
dummy = None
for line in sys.stdin:
dummy = line.split()
count += 1
delta_sec = int(time.time() - start_time)
print("Python: Saw {0} lines in {1} seconds. ".format(count, delta_sec), end='')
if delta_sec > 0:
lps = int(count/delta_sec)
print(" Crunch Speed: {0}".format(lps))
else:
print('')
C ++ 코드 :
#include <iostream>
#include <string>
#include <sstream>
#include <time.h>
#include <vector>
using namespace std;
void split1(vector<string> &tokens, const string &str,
const string &delimiters = " ") {
// Skip delimiters at beginning
string::size_type lastPos = str.find_first_not_of(delimiters, 0);
// Find first non-delimiter
string::size_type pos = str.find_first_of(delimiters, lastPos);
while (string::npos != pos || string::npos != lastPos) {
// Found a token, add it to the vector
tokens.push_back(str.substr(lastPos, pos - lastPos));
// Skip delimiters
lastPos = str.find_first_not_of(delimiters, pos);
// Find next non-delimiter
pos = str.find_first_of(delimiters, lastPos);
}
}
void split2(vector<string> &tokens, const string &str, char delim=' ') {
stringstream ss(str); //convert string to stream
string item;
while(getline(ss, item, delim)) {
tokens.push_back(item); //add token to vector
}
}
int main() {
string input_line;
vector<string> spline;
long count = 0;
int sec, lps;
time_t start = time(NULL);
cin.sync_with_stdio(false); //disable synchronous IO
while(cin) {
getline(cin, input_line);
spline.clear(); //empty the vector for the next line to parse
//I'm trying one of the two implementations, per compilation, obviously:
// split1(spline, input_line);
split2(spline, input_line);
count++;
};
count--; //subtract for final over-read
sec = (int) time(NULL) - start;
cerr << "C++ : Saw " << count << " lines in " << sec << " seconds." ;
if (sec > 0) {
lps = count / sec;
cerr << " Crunch speed: " << lps << endl;
} else
cerr << endl;
return 0;
//compiled with: g++ -Wall -O3 -o split1 split_1.cpp
두 가지 분할 구현을 시도했습니다. 한 (split1)는 토큰을 검색 문자열 방법을 사용하고 여러 토큰뿐만 아니라 핸들 많은 토큰 (그것에서 오는 병합 할 수 있습니다 여기 ). 두 번째 (split2)는 getline을 사용하여 문자열을 스트림으로 읽고 구분 기호를 병합하지 않으며 단일 구분 문자 만 지원합니다 (문자열 분할 질문에 대한 답변으로 여러 StackOverflow 사용자가 게시 한 문자).
나는 이것을 다양한 순서로 여러 번 실행했습니다. 내 테스트 머신은 Macbook Pro (2011, 8GB, Quad Core)이지만 그다지 중요하지 않습니다. 나는 각각 다음과 비슷한 3 개의 공백으로 구분 된 열이있는 20M 줄 텍스트 파일로 테스트하고 있습니다. "foo.bar 127.0.0.1 home.foo.bar"
결과 :
$ /usr/bin/time cat test_lines_double | ./split.py
15.61 real 0.01 user 0.38 sys
Python: Saw 20000000 lines in 15 seconds. Crunch Speed: 1333333
$ /usr/bin/time cat test_lines_double | ./split1
23.50 real 0.01 user 0.46 sys
C++ : Saw 20000000 lines in 23 seconds. Crunch speed: 869565
$ /usr/bin/time cat test_lines_double | ./split2
44.69 real 0.02 user 0.62 sys
C++ : Saw 20000000 lines in 45 seconds. Crunch speed: 444444
내가 뭘 잘못하고 있죠? 외부 라이브러리에 의존하지 않고 (즉, 부스트 없음), 구분 기호 시퀀스 병합을 지원하고 (파이썬 분할과 같은) 스레드로부터 안전하며 (따라서 strtok가 없음) 성능이 최소한 인 C ++에서 문자열 분할을 수행하는 더 좋은 방법이 있습니까? 파이썬과 동등합니까?
1 / 부분 솔루션 편집? :
C ++처럼 파이썬이 더미 목록을 재설정하고 매번 추가하도록하여 더 공정한 비교를 시도했습니다. 이것은 여전히 C ++ 코드가 정확히하는 것은 아니지만 조금 더 가깝습니다. 기본적으로 루프는 다음과 같습니다.
for line in sys.stdin:
dummy = []
dummy += line.split()
count += 1
파이썬의 성능은 이제 split1 C ++ 구현과 거의 같습니다.
/usr/bin/time cat test_lines_double | ./split5.py
22.61 real 0.01 user 0.40 sys
Python: Saw 20000000 lines in 22 seconds. Crunch Speed: 909090
Python이 문자열 처리에 최적화되어 있어도 (Matt Joiner가 제안한대로) 이러한 C ++ 구현이 더 빠르지 않다는 사실에 여전히 놀랍습니다. C ++를 사용하여보다 최적의 방법으로이 작업을 수행하는 방법에 대한 아이디어가있는 사람이 있으면 코드를 공유하십시오. (내 다음 단계는 C로 전체 프로젝트를 다시 구현하기 위해 프로그래머 생산성을 절충하지 않더라도 순수 C로 구현하려고 시도 할 것이라고 생각합니다. 따라서 이것은 문자열 분할 속도에 대한 실험 일뿐입니다.)
도움을 주신 모든 분들께 감사드립니다.
최종 편집 / 솔루션 :
Alf의 대답을 참조하십시오. 파이썬은 엄격하게 참조로 문자열을 처리하고 STL 문자열은 자주 복사되기 때문에 바닐라 파이썬 구현에서 성능이 더 좋습니다. 비교를 위해 Alf의 코드를 통해 데이터를 컴파일하고 실행했습니다. 여기에는 다른 모든 실행과 동일한 시스템에서의 성능이 있습니다. 기본적으로 순진한 Python 구현과 동일합니다 (목록을 재설정 / 추가하는 Python 구현보다 빠르지 만, 위의 편집에 표시됨) :
$ /usr/bin/time cat test_lines_double | ./split6
15.09 real 0.01 user 0.45 sys
C++ : Saw 20000000 lines in 15 seconds. Crunch speed: 1333333
이 경우에 C ++를 수행하는 데 필요한 코드의 양에 관한 유일한 불만 사항이 남아 있습니다.
이 문제와 어제의 stdin 행 읽기 문제 (위에 링크 됨)에서 얻은 교훈 중 하나는 언어의 상대적인 "기본"성능에 대해 순진한 가정을하는 대신 항상 벤치마킹해야한다는 것입니다. 교육에 감사드립니다.
귀하의 제안에 다시 한번 감사드립니다!
g++ -Wall -O3 -o split1 split_1.cpp
@JJC : 벤치 마크 요금이 때 실제로 사용 않는 방법 dummy
과 spline
각각 어쩌면 파이썬에 전화를 제거 line.split()
하여 더 부작용이 없기 때문에?