मैं थोड़ी गति हासिल करने और अपने जंग खाए C ++ कौशल को तेज करने के प्रयास में कुछ कोड को पायथन से C ++ में बदलने की कोशिश कर रहा हूं। मैं कल हैरान जब stdin से लाइनों को पढ़ने के एक अनुभवहीन कार्यान्वयन बहुत तेजी से सी से अजगर में था ++ (देखें था इस )। आज, मुझे अंत में पता चला कि सी + + में विलय करने वाले सीमांकक के साथ एक स्ट्रिंग को कैसे विभाजित किया जाता है (अजगर के समान () के समान शब्दार्थ), और अब मैं deja vu का अनुभव कर रहा हूं! मेरा C ++ कोड काम करने में अधिक समय लेता है (हालांकि अधिक परिमाण का क्रम नहीं है, जैसा कि कल के पाठ के लिए था)।
पायथन कोड:
#!/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) टोकन खोजने के लिए स्ट्रिंग विधियों का उपयोग करता है और कई टोकन को मर्ज करने में सक्षम है और साथ ही कई टोकन संभालता है (यह यहां से आता है )। दूसरी (स्प्लिट 2) स्ट्रिंग को स्ट्रीम के रूप में पढ़ने के लिए गेटलाइन का उपयोग करता है, सीमांकक का विलय नहीं करता है, और केवल एक एकल परिधि चरित्र का समर्थन करता है (जो कि स्ट्रिंग विभाजन के सवालों के जवाब में कई StackOverflow उपयोगकर्ताओं द्वारा पोस्ट किया गया था)।
मैंने इसे कई बार विभिन्न आदेशों में चलाया। मेरी टेस्ट मशीन मैकबुक प्रो (2011, 8 जीबी, क्वाड कोर) है, न कि यह बहुत मायने रखती है। मैं 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
मैं क्या गलत कर रहा हूं? क्या सी ++ में स्ट्रिंग विभाजन करने का एक बेहतर तरीका है जो बाहरी पुस्तकालयों (यानी कोई बढ़ावा नहीं) पर निर्भर नहीं करता है, सीमांकक के विलय के अनुक्रम का समर्थन करता है (जैसे कि अजगर का विभाजन), धागा सुरक्षित है (इसलिए कोई स्ट्रैटोक), और जिसका प्रदर्शन कम से कम है अजगर के साथ बराबर पर?
संपादित करें 1 / आंशिक समाधान ?:
मैंने कोशिश की कि अजगर की डमी लिस्ट को रीसेट करके इसे और अधिक निष्पक्ष बनाया जाए और हर बार इसे जोड़ा जाए, जैसा कि C ++ करता है। यह अभी भी ठीक वैसा नहीं है जैसा कि C ++ कोड कर रहा है, लेकिन यह थोड़ा करीब है। असल में, लूप अब है:
for line in sys.stdin:
dummy = []
dummy += line.split()
count += 1
अजगर का प्रदर्शन अब विभाजन 1 सी ++ कार्यान्वयन के समान है।
/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
मुझे अभी भी आश्चर्य है कि, भले ही पायथन स्ट्रिंग प्रक्रिया के लिए अनुकूलित है (जैसा कि मैट जॉइनर ने सुझाव दिया है), कि ये सी ++ कार्यान्वयन तेजी से नहीं होंगे। यदि किसी के पास C ++ का उपयोग करके अधिक इष्टतम तरीके से यह करने के बारे में विचार हैं, तो कृपया अपना कोड साझा करें। (मुझे लगता है कि मेरा अगला कदम शुद्ध सी में इसे लागू करने की कोशिश कर रहा होगा, हालांकि मैं सी में अपनी समग्र परियोजना को फिर से लागू करने के लिए प्रोग्रामर उत्पादकता से व्यापार करने नहीं जा रहा हूं, इसलिए यह केवल स्ट्रिंग विभाजन की गति के लिए एक प्रयोग होगा।)
मदद के लिए आप सबका शुक्रिया।
अंतिम संपादन / समाधान:
कृपया अल्फ का स्वीकृत उत्तर देखें। चूंकि अजगर संदर्भ के साथ सख्ती से स्ट्रिंग्स के साथ व्यवहार करता है और एसटीएल स्ट्रिंग्स को अक्सर कॉपी किया जाता है, इसलिए प्रदर्शन वेनिला पायथन कार्यान्वयन के साथ बेहतर होता है। तुलना के लिए, मैंने अल्फ कोड के माध्यम से अपना डेटा संकलित और चलाया, और यहां उसी मशीन पर प्रदर्शन किया गया है, जो अन्य सभी रन के रूप में है, अनिवार्य रूप से भोले अजगर के कार्यान्वयन के समान है (हालांकि अजगर कार्यान्वयन की तुलना में तेज़ है जो सूची को रीसेट / अपेंड करता है) उपरोक्त संपादन में दिखाया गया है):
$ /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 ++ प्राप्त करने के लिए आवश्यक कोड की मात्रा के बारे में है।
इस मुद्दे से एक सबक और कल की स्टड लाइन रीडिंग मुद्दा (ऊपर जुड़ा हुआ) यह है कि भाषाओं के सापेक्ष "डिफ़ॉल्ट" प्रदर्शन के बारे में भोली धारणा बनाने के बजाय हमेशा बेंचमार्क करना चाहिए। मैं शिक्षा की सराहना करता हूं।
आपके सुझाव के लिए फिर से धन्यवाद!