Biraz hız kazanmak ve paslı C ++ becerilerimi geliştirmek için bazı kodları Python'dan C ++ 'ya dönüştürmeye çalışıyorum. Stdin'den satırları okurken bir naif uygulama (bkz ++ çok daha hızlı C'nin Python iken Dün şok oldu bu ). Bugün nihayet C ++ 'da bir dizeyi sınırlayıcıları birleştirerek (python split () ile benzer anlambilim) nasıl böleceğimi buldum ve şimdi deja vu yaşıyorum! C ++ kodumun işi yapması çok daha uzun sürüyor (dünkü derste olduğu gibi bir büyüklük sırası olmasa da).
Python Kodu:
#!/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 ++ Kodu:
#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
İki farklı split uygulaması denediğime dikkat edin. Bir (split1) jeton aramak için dize yöntemleri kullanır ve birden belirteçleri yanı sıra sap sayısız belirteçleri (o geliyor birleştirme yapabiliyor burada ). İkincisi (split2), dizeyi bir akış olarak okumak için getline kullanır, sınırlayıcıları birleştirmez ve yalnızca tek bir sınırlayıcı karakteri destekler (bu, birkaç StackOverflow kullanıcısı tarafından dize bölme sorularının yanıtlarında yayınlanmıştır).
Bunu çeşitli siparişlerde defalarca çalıştırdım. Test makinem bir Macbook Pro (2011, 8GB, Dört Çekirdekli), o kadar önemli değil. Her biri şuna benzer görünen boşlukla ayrılmış üç sütuna sahip 20M satırlık bir metin dosyasıyla test ediyorum: "foo.bar 127.0.0.1 home.foo.bar"
Sonuçlar:
$ /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
Neyi yanlış yapıyorum? C ++ 'da dize bölme yapmanın harici kitaplıklara dayanmayan (yani yükseltme yok), sınırlayıcı dizilerini birleştirmeyi destekleyen (python bölme gibi), iş parçacığı güvenli (yani strtok yok) ve performansı en az olan daha iyi bir yolu var mı python ile eşit mi?
Düzenleme 1 / Kısmi Çözüm ?:
Python'un kukla listeyi sıfırlamasını ve C ++ 'nın yaptığı gibi her seferinde buna eklemesini sağlayarak daha adil bir karşılaştırma yapmayı denedim. Bu hala C ++ kodunun yaptığı şey değil, ancak biraz daha yakın. Temel olarak, döngü şimdi:
for line in sys.stdin:
dummy = []
dummy += line.split()
count += 1
Python'un performansı artık split1 C ++ uygulamasıyla hemen hemen aynı.
/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 dizge işleme için bu kadar optimize edilmiş olsa bile (Matt Joiner'ın önerdiği gibi) bu C ++ uygulamalarının daha hızlı olmayacağına hala şaşırıyorum. C ++ kullanarak bunu daha optimal bir şekilde nasıl yapacağınıza dair bir fikri olan varsa, lütfen kodunuzu paylaşın. (Sanırım bir sonraki adımım bunu saf C'de uygulamaya çalışmak olacak, ancak programcı verimliliğini C'deki genel projemi yeniden uygulamak için ödün vermeyeceğim, bu yüzden bu sadece dizi bölme hızı için bir deney olacak.)
Yardımlarınız için hepinize teşekkürler.
Son Düzenleme / Çözüm:
Lütfen Alf'ın kabul ettiği cevaba bakın. Python dizelerle kesinlikle referansla ilgilendiğinden ve STL dizeleri genellikle kopyalandığından, vanilya python uygulamalarıyla performans daha iyidir. Karşılaştırma için, verilerimi Alf'ın kodu aracılığıyla derledim ve çalıştırdım ve işte diğer tüm çalıştırmalarla aynı makinedeki performans, temelde saf python uygulamasıyla aynıdır (ancak listeyi sıfırlayan / ekleyen python uygulamasından daha hızlıdır. yukarıdaki düzenlemede gösterilmiştir):
$ /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
Geriye kalan tek küçük sıkıntım, bu durumda C ++ 'nın gerçekleştirilmesi için gereken kod miktarı ile ilgilidir.
Bu sayıdan ve dünün standart satır okuma sorunundan (yukarıda bağlantılı) alınan derslerden biri, dillerin göreceli "varsayılan" performansı hakkında naif varsayımlar yapmak yerine her zaman kıyaslama yapılması gerektiğidir. Eğitimi takdir ediyorum.
Önerileriniz için hepinize tekrar teşekkürler!