From ffecc3df8c3f63443e32b3c3b10ed41dde055d9d Mon Sep 17 00:00:00 2001 From: Vivek N Date: Sun, 29 May 2016 16:19:14 +0530 Subject: [PATCH 1/3] Add two implementations split10 and split11 ================================================== split10 --------- This is a simple implementation that uses std::find() It uses a for loop with two variables: itStart - iterator start of a word itDelimiter - iterator to a delimiter We get the result of find() for the next delimiter in itDelim and then save the range (itStart, itDelim) as a token, and emplace the string into the result vector This very simple 5 liner shows that good C++ code can beat C code without any trouble -------------------------------------------------------------------------- split11 ---------- Almost the same as split10 but we use a StringRef class just like the one in split6. Instead of iterators we use a char pointer This speeds it up by almost a factor of 80% We get pretty close to the subparser version, only about 15% slower on my system. -------------------------------------------------------------------------- Benchmark results (trimmed output for 80 columns) ------------------------------------------------- $ ./run_all.bash === System info Arch rolling Linux 4.5.5-1-ck x86_64 GNU/Linux Intel(R) Core(TM) CPU X 920 @ 2.00GHz g++ (GCC) 6.1.1 20160501 Python 3.5.1 === End System info ./split.py Python: 38.9 seconds. Crunch Speed: 514293.2 ./split5.py Python: 41.0 seconds. Crunch Speed: 488168.6 ./split1 C++ : 8.7 seconds. Crunch speed: 2288344.6 ./split2 C++ : 20.9 seconds. Crunch speed: 958164.2 ./split6 C++ : 3.6 seconds. Crunch speed: 5603155.4 ./split7 C++ : 2.6 seconds. Crunch speed: 7750547.7 ./split8 C++ : 31.0 seconds. Crunch speed: 644411.0 ./split9 C++ : 21.1 seconds. Crunch speed: 949104.7 ./split10 C++ : 3.7 seconds. Crunch speed: 5387448.0 ./split11 C++ : 2.3 seconds. Crunch speed: 8703679.3 ./split_subparser C++ : 2.0 seconds. Crunch speed: 9956735.4 ./splitc1 C++ : 7.9 seconds. Crunch speed: 2519434.9 ./splitc2 C++ : 8.0 seconds. Crunch speed: 2484935.2 ./splitc3 C++ : 8.0 seconds. Crunch speed: 2515293.7 $ --- Makefile | 2 +- split10.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++ split11.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 split10.cpp create mode 100644 split11.cpp diff --git a/Makefile b/Makefile index 8be0a33..c361138 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CXXFLAGS = -Wall -O3 -all: split1 split2 split6 split7 split8 split9 splitc1 splitc2 splitc3 split_subparser +all: split1 split2 split6 split7 split8 split9 splitc1 splitc2 splitc3 split_subparser split10 split11 split7: split7.cpp | deps/strtk $(CXX) $(LDFLAGS) -Ideps/strtk/ $(CXXFLAGS) split7.cpp -o split7 diff --git a/split10.cpp b/split10.cpp new file mode 100644 index 0000000..13a1968 --- /dev/null +++ b/split10.cpp @@ -0,0 +1,54 @@ +// http://stackoverflow.com/a/9379203/106471 +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +void split(vector &ret, const string& s, char delimiter=' ') +{ + ret.clear(); + for(auto itStart = s.begin(), itDelim = itStart, itEnd = s.end(); itStart < itEnd; itStart = itDelim + 1) + { + itDelim = find(itStart, itEnd, delimiter); + ret.emplace_back(string(itStart, itDelim)); + } +} + + + +int main() { + string input_line; + long count = 0; + timespec start; + clock_gettime(CLOCK_MONOTONIC, &start); + + cin.sync_with_stdio(false); //disable synchronous IO + size_t numWords = 0; + size_t numChars = 0; + + vector words; + while(getline(cin, input_line)) + { + split(words, input_line); + numWords += words.size(); + for(const auto &s:words) + numChars += s.size(); + count++; + }; + + timespec end; + clock_gettime(CLOCK_MONOTONIC, &end); + const double sec = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) * 1e-9; + cerr << "C++ : Saw " << count << " lines (" << numWords << " words/" << numChars << " chars) in " << fixed << setprecision(1) << sec << " seconds." ; + if (sec > 0) { + const double lps = count / sec; + cerr << " Crunch speed: " << fixed << setprecision(1) << lps << endl; + } else + cerr << endl; + return 0; +} diff --git a/split11.cpp b/split11.cpp new file mode 100644 index 0000000..c25bb1d --- /dev/null +++ b/split11.cpp @@ -0,0 +1,68 @@ +// http://stackoverflow.com/a/9379203/106471 +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class StringRef +{ +private: + const char* m_itBeg; + int m_iSize; + +public: + int size() const { return m_iSize; } + const char* begin() { return m_itBeg; } + const char* end() const { return m_itBeg + m_iSize; } + + StringRef(const char* &beg, const char* &nd) + : m_itBeg(beg) + , m_iSize(nd - beg) + {} +}; + +void split(vector &ret, const string& s) +{ + for(auto ps = &s[0], pd = ps, pe = ps + s.size(); ps < pe; ps = pd + 1) + { + ret.emplace_back(StringRef(ps, pd = find(ps, pe, ' '))); + } +} + +int main() { + string input_line; + long count = 0; + timespec start; + clock_gettime(CLOCK_MONOTONIC, &start); + + cin.sync_with_stdio(false); //disable synchronous IO + size_t numWords = 0; + size_t numChars = 0; + + vector words; + while(getline(cin, input_line)) + { + words.clear(); + split(words, input_line); + numWords += words.size(); + for(const auto &s:words) + numChars += s.size(); + count++; + }; + + timespec end; + clock_gettime(CLOCK_MONOTONIC, &end); + const double sec = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) * 1e-9; + cerr << "C++ : Saw " << count << " lines (" << numWords << " words/" << numChars << " chars) in " << fixed << setprecision(1) << sec << " seconds." ; + if (sec > 0) { + const double lps = count / sec; + cerr << " Crunch speed: " << fixed << setprecision(1) << lps << endl; + } else + cerr << endl; + return 0; +} From 59b1885e6bc8376a69615dc315040ddcc329b6fb Mon Sep 17 00:00:00 2001 From: Vivek N Date: Mon, 30 May 2016 01:57:35 +0530 Subject: [PATCH 2/3] split1 - Make minor tweaks --- split11.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/split11.cpp b/split11.cpp index c25bb1d..4e991b2 100644 --- a/split11.cpp +++ b/split11.cpp @@ -16,7 +16,7 @@ class StringRef int m_iSize; public: - int size() const { return m_iSize; } + inline int size() const { return m_iSize; } const char* begin() { return m_itBeg; } const char* end() const { return m_itBeg + m_iSize; } @@ -26,12 +26,13 @@ class StringRef {} }; -void split(vector &ret, const string& s) +int split(vector &ret, const string& s, const char delimiter = ' ') { for(auto ps = &s[0], pd = ps, pe = ps + s.size(); ps < pe; ps = pd + 1) { - ret.emplace_back(StringRef(ps, pd = find(ps, pe, ' '))); + ret.emplace_back(StringRef(ps, pd = find(ps, pe, delimiter))); } + return ret.size(); } int main() { @@ -45,11 +46,11 @@ int main() { size_t numChars = 0; vector words; + words.reserve(100); while(getline(cin, input_line)) { words.clear(); - split(words, input_line); - numWords += words.size(); + numWords += split(words, input_line); for(const auto &s:words) numChars += s.size(); count++; From 2ddaba0b8ed2dad91b8226894d365d9cf64382c1 Mon Sep 17 00:00:00 2001 From: Vivek N Date: Mon, 30 May 2016 02:00:28 +0530 Subject: [PATCH 3/3] Add split12 Same as split11 except use memchr() Now this is as fast or faster than split_subparser -march=SSE2 and so on might be even faster --- Makefile | 4 +-- split12.cpp | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 split12.cpp diff --git a/Makefile b/Makefile index c361138..67782a7 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CXXFLAGS = -Wall -O3 -all: split1 split2 split6 split7 split8 split9 splitc1 splitc2 splitc3 split_subparser split10 split11 +all: split1 split2 split6 split7 split8 split9 splitc1 splitc2 splitc3 split_subparser split10 split11 split12 split7: split7.cpp | deps/strtk $(CXX) $(LDFLAGS) -Ideps/strtk/ $(CXXFLAGS) split7.cpp -o split7 @@ -12,7 +12,7 @@ split_subparser: split_subparser.cpp | deps/json_parser .PHONY: clean update-deps clean: - @rm -f split1 split2 split6 split7 split8 split9 splitc1 splitc2 splitc3 split_subparser + @rm -f split1 split2 split6 split7 split8 split9 splitc1 splitc2 splitc3 split_subparser split10 split11 split12 update-deps: deps git --git-dir=deps/strtk/.git/ --work-tree=deps/strtk/ fetch origin diff --git a/split12.cpp b/split12.cpp new file mode 100644 index 0000000..22a4bb0 --- /dev/null +++ b/split12.cpp @@ -0,0 +1,72 @@ +// http://stackoverflow.com/a/9379203/106471 +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class StringRef +{ +private: + const char* m_itBeg; + int m_iSize; + +public: + inline int size() const { return m_iSize; } + const char* begin() { return m_itBeg; } + const char* end() const { return m_itBeg + m_iSize; } + + StringRef(const char* &beg, const char* &nd) + : m_itBeg(beg) + , m_iSize(nd - beg) + {} +}; + +int split(vector &ret, const string& s, const char delimiter = ' ') +{ + auto ps = &s[0], pd = ps, pe = ps + s.size(); + for(;pd; ps = pd + 1) + { + pd = (const char *)memchr(ps, delimiter, pe - ps); + ret.emplace_back(StringRef(ps, pd ? pd : pe)); + } + return ret.size(); +} + +int main() { + string input_line; + long count = 0; + timespec start; + clock_gettime(CLOCK_MONOTONIC, &start); + + cin.sync_with_stdio(false); //disable synchronous IO + size_t numWords = 0; + size_t numChars = 0; + + vector words; + words.reserve(100); + while(getline(cin, input_line)) + { + words.clear(); + numWords += split(words, input_line); + for(const auto &s:words) + numChars += s.size(); + count++; + }; + + timespec end; + clock_gettime(CLOCK_MONOTONIC, &end); + const double sec = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) * 1e-9; + cerr << "C++ : Saw " << count << " lines (" << numWords << " words/" << numChars << " chars) in " << fixed << setprecision(1) << sec << " seconds." ; + if (sec > 0) { + const double lps = count / sec; + cerr << " Crunch speed: " << fixed << setprecision(1) << lps << endl; + } else + cerr << endl; + return 0; +}