Saya mencoba mengonversi beberapa kode dari Python ke C ++ dalam upaya untuk mendapatkan sedikit kecepatan dan mempertajam keterampilan C ++ saya yang berkarat. Kemarin saya terkejut ketika implementasi naif membaca baris dari stdin jauh lebih cepat dengan Python daripada C ++ (lihat ini ). Hari ini, saya akhirnya menemukan cara membagi string di C ++ dengan pembatas penggabungan (semantik mirip dengan split python ()), dan sekarang saya mengalami deja vu! Kode C ++ saya membutuhkan waktu lebih lama untuk melakukan pekerjaan itu (meskipun bukan urutan besarnya, seperti yang terjadi pada pelajaran kemarin).
Kode 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('')
Kode 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
Perhatikan bahwa saya mencoba dua implementasi pemisahan yang berbeda. One (split1) menggunakan metode string untuk mencari token dan mampu menggabungkan banyak token serta menangani banyak token (berasal dari sini ). Yang kedua (split2) menggunakan getline untuk membaca string sebagai aliran, tidak menggabungkan pembatas, dan hanya mendukung satu karakter pembatas (yang diposting oleh beberapa pengguna StackOverflow sebagai jawaban untuk pertanyaan pemisahan string).
Saya menjalankan ini beberapa kali dalam berbagai pesanan. Mesin uji saya adalah Macbook Pro (2011, 8GB, Quad Core), bukan itu masalahnya. Saya menguji dengan file teks baris 20 juta dengan tiga kolom terpisah spasi yang masing-masing terlihat mirip dengan ini: "foo.bar 127.0.0.1 home.foo.bar"
Hasil:
$ /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
Apa yang saya lakukan salah? Apakah ada cara yang lebih baik untuk melakukan pemisahan string dalam C ++ yang tidak bergantung pada pustaka eksternal (yaitu, tidak ada dorongan), mendukung penggabungan urutan pembatas (seperti pemisahan python), aman untuk utas (jadi tidak ada strtok), dan yang kinerjanya setidaknya setara dengan python?
Edit 1 / Solusi Parsial ?:
Saya mencoba membuatnya menjadi perbandingan yang lebih adil dengan meminta python mengatur ulang daftar boneka dan menambahkannya setiap kali, seperti yang dilakukan C ++. Ini masih belum persis seperti yang dilakukan kode C ++, tetapi ini sedikit lebih dekat. Pada dasarnya, loop sekarang:
for line in sys.stdin:
dummy = []
dummy += line.split()
count += 1
Kinerja python sekarang hampir sama dengan implementasi 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
Saya masih terkejut bahwa, meskipun Python begitu dioptimalkan untuk pemrosesan string (seperti yang disarankan Matt Joiner), implementasi C ++ ini tidak akan lebih cepat. Jika ada yang punya ide tentang cara melakukan ini dengan cara yang lebih optimal menggunakan C ++, silakan bagikan kode Anda. (Saya pikir langkah saya selanjutnya akan mencoba menerapkan ini dalam C murni, meskipun saya tidak akan menukar produktivitas pemrogram untuk menerapkan ulang keseluruhan proyek saya di C, jadi ini hanya akan menjadi eksperimen untuk kecepatan pemisahan string.)
Terima kasih untuk semua atas bantuan Anda.
Edit / Solusi Akhir:
Silakan lihat jawaban Alf yang diterima. Karena python menangani string secara ketat dengan referensi dan string STL sering disalin, kinerja lebih baik dengan implementasi vanilla python. Sebagai perbandingan, saya mengumpulkan dan menjalankan data saya melalui kode Alf, dan berikut adalah kinerja pada mesin yang sama dengan semua yang berjalan, pada dasarnya identik dengan implementasi python naif (meskipun lebih cepat daripada implementasi python yang menyetel ulang / menambahkan daftar, seperti ditampilkan di edit di atas):
$ /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
Keluhan kecil saya yang tersisa hanyalah mengenai jumlah kode yang diperlukan untuk mendapatkan C ++ untuk bekerja dalam kasus ini.
Salah satu pelajaran di sini dari masalah ini dan masalah membaca garis stdin kemarin (ditautkan di atas) adalah bahwa seseorang harus selalu membuat tolok ukur daripada membuat asumsi naif tentang kinerja relatif "default" bahasa. Saya menghargai pendidikan.
Terima kasih sekali lagi untuk semua saran Anda!