ฉันกำลังพยายามแปลงรหัสจาก Python เป็น C ++ เพื่อเพิ่มความเร็วและเพิ่มทักษะ C ++ ที่เป็นสนิมของฉัน เมื่อวานนี้ฉันรู้สึกตกใจเมื่อการใช้งานบรรทัดการอ่านอย่างไร้เดียงสาจาก stdin ใน Python เร็วกว่า C ++ มาก (ดูสิ่งนี้ ) วันนี้ในที่สุดฉันก็ค้นพบวิธีการแยกสตริงใน C ++ ด้วยการรวมตัวคั่น (ความหมายที่คล้ายกันกับการแยกของ python ()) และตอนนี้ฉันกำลังประสบกับ 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
โปรดทราบว่าฉันได้ลองใช้การแยกสองแบบที่แตกต่างกัน หนึ่ง (แยก 1) ใช้วิธีการสตริงเพื่อค้นหาโทเค็นและสามารถรวมโทเค็นหลายตัวรวมทั้งจัดการโทเค็นจำนวนมากได้ (มาจากที่นี่ ) ตัวที่สอง (split2) ใช้ getline เพื่ออ่านสตริงเป็นสตรีมไม่รวมตัวคั่นและรองรับเฉพาะอักขระเดลิมิเตอร์ตัวเดียว (อันนี้โพสต์โดยผู้ใช้ StackOverflow หลายรายในคำตอบสำหรับคำถามการแยกสตริง)
ฉันทำงานนี้หลายครั้งในคำสั่งต่างๆ เครื่องทดสอบของฉันคือ Macbook Pro (2011, 8GB, Quad Core) ไม่ใช่ว่าจะสำคัญมาก ฉันกำลังทดสอบกับไฟล์ข้อความบรรทัด 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
ผมทำอะไรผิดหรือเปล่า? มีวิธีที่ดีกว่าไหมในการแยกสตริงใน C ++ ที่ไม่ต้องอาศัยไลบรารีภายนอก (เช่นไม่มีการเพิ่ม) รองรับการรวมลำดับของตัวคั่น (เช่นการแยกของ python) เธรดที่ปลอดภัย (ดังนั้นจึงไม่มี strtok) และมีประสิทธิภาพอย่างน้อยที่สุด พอ ๆ กับ python?
แก้ไข 1 / โซลูชันบางส่วน?:
ฉันพยายามทำให้เป็นการเปรียบเทียบที่ยุติธรรมยิ่งขึ้นโดยให้ python รีเซ็ตรายการดัมมี่และต่อท้ายทุกครั้งตามที่ C ++ ทำ นี่ยังไม่ตรงกับสิ่งที่โค้ด C ++ กำลังทำอยู่ แต่ใกล้กว่าเล็กน้อย โดยทั่วไปลูปตอนนี้คือ:
for line in sys.stdin:
dummy = []
dummy += line.split()
count += 1
ขณะนี้ประสิทธิภาพของ python ใกล้เคียงกับการใช้งาน C ++ ของ Split1
/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 เนื่องจาก python เกี่ยวข้องกับสตริงอย่างเคร่งครัดโดยการอ้างอิงและสตริง STL มักจะถูกคัดลอกประสิทธิภาพจะดีกว่าเมื่อใช้งาน vanilla python สำหรับการเปรียบเทียบฉันรวบรวมและเรียกใช้ข้อมูลของฉันผ่านรหัสของ 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 ของเมื่อวาน (ลิงก์ด้านบน) คือควรใช้เกณฑ์มาตรฐานแทนที่จะตั้งสมมติฐานที่ไร้เดียงสาเกี่ยวกับประสิทธิภาพ "ค่าเริ่มต้น" ที่สัมพันธ์กันของภาษา ขอชื่นชมการศึกษา
ขอขอบคุณอีกครั้งสำหรับคำแนะนำของคุณ!