Monthly ArchiveFebruary 2010
C++ 16 Feb 2010 19:58:03
C++ Convert String to Int Speed
(There is also an opposite int-to-string performance test.)
A performance benchmark of which method is faster of converting an std::string to an integer. The goal is ending up with an integer with the value represented in an std::string.
The tested methods are:
- atoi()
- atol()
- strtol()
- std::stringstream
- std::stringstream, reusing the object
- boost::lexical_cast<int>()
- a hand-written naive loop
Source for the test is at speed-string-to-int.cpp with cycle.h.
The compilers are Microsoft Visual C++ 2010 Express as VC10 with _SECURE_SCL disabled, and GNU g++ 4.3.2.
Tests were run for converting 100000 string containing integers in the range -50000 to 50000. The result for atoi() is set as the baseline 100% and the other numbers is time spent relative to that. The naive loop wins by a large margin.
| VC++ 2010 | |
|---|---|
| atoi() | 100.00% |
| atol() | 96.73% |
| strtol() | 93.94% |
| stringstream | 5164.50% |
| stringstream reused | 2817.94% |
| boost::lexical_cast | 4471.66% |
| naive loop | 18.43% |
| g++ 4.3.2 | |
|---|---|
| atoi() | 100.00% |
| atol() | 100.47% |
| strtol() | 97.00% |
| stringstream | 2040.35% |
| stringstream reused | 482.86% |
| boost::lexical_cast | 1004.41% |
| naive loop | 37.39% |
C++ 07 Feb 2010 15:41:15
C++ Convert Int to String Speed
(There is also an opposite string-to-int performance test.)
(Updated 2010-03-05 to compare speeds of reusing the string object versus not, and to add strstream and boost::spirit::karma tests.)
A performance benchmark of which method is faster of converting an integer to an std::string. The goal is ending up with an std::string representation of the input integer.
The tested methods are:
- sprintf() into a char[] buffer, then std::string(buffer)
- snprintf() into a char[] buffer, then std::string(buffer)
- sprintf() into &std::string[0], and .resize() to fit
- snprintf() into &std::string[0], and .resize() to fit
- output to a std::stringstream, then std::string = stream.str()
- as above, but reusing the stringstream object
- std::strstream(&string[0]) then .resize(stream.pcount())
- std::string = boost::lexical_cast<std::string>()
- Boost.Spirit.Karma generate into a char[] buffer, then std::string(buffer)
Source for the test is at speed-convert-int-to-string.cpp with cycle.h.
The compilers are Microsoft Visual C++ 2010 Express as VC10 with _SECURE_SCL disabled, GNU g++ 4.3.2, and clang++ from svn.
Tests were run for converting 100000 integers in the range -50000 to 50000. The result for sprintf() is set as the baseline 100% and the other numbers is time spent relative to that.
Boost.Spirit.Karma is by far the fastest. It is also worth noting that reusing the string object makes the boost::lexical_cast solution slower, presumably because it ruins some optimization opportunities, so that is a case for checking whether reusing your objects really is faster…
| VC++ 2010, Boost 1.42 | Percentage |
|---|---|
| sprintf char[] (reuse string) | 100.00% |
| sprintf char[] (new string) | 100.39% |
| snprintf char[] (reuse string) | 99.39% |
| sprintf &string[0] (reuse string) | 103.22% |
| snprintf &string[0] (reuse string) | 103.85% |
| stringstream (new stream, reuse string) | 502.88% |
| stringstream (new stream, new string) | 507.08% |
| stringstream (reuse stream, reuse string) | 278.56% |
| stringstream (reuse stream, new string) | 280.29% |
| strstream (reuse stream, internal buffer) | 611.35% |
| strstream (reuse stream, string buffer) | 621.25% |
| lexical_cast (reuse string) | 72.99% |
| lexical_cast (new string) | 69.72% |
| karma (reuse string) | 9.52% |
| karma (new string) | 10.17% |
| Fedora g++ 4.3.2, Boost 1.42 | Percentage |
|---|---|
| sprintf char[] (reuse string) | 100.00% |
| sprintf char[] (new string) | 124.93% |
| snprintf char[] (reuse string) | 99.21% |
| sprintf &string[0] (reuse string) | 103.14% |
| snprintf &string[0] (reuse string) | 105.37% |
| stringstream (new stream, reuse string) | 465.73% |
| stringstream (new stream, new string) | 471.35% |
| stringstream (reuse stream, reuse string) | 137.44% |
| stringstream (reuse stream, new string) | 131.40% |
| strstream (new stream, internal buffer) | 463.57% |
| strstream (reuse stream, string buffer) | 285.54% |
| lexical_cast (reuse string) | 96.26% |
| lexical_cast (new string) | 89.18% |
| karma (reuse string) | 32.79% |
| karma (new string) | 61.48% |
The clang++ results are directly comparable with the g++ results; run on the same machine, and the sprintf() results deviate a mere 0.07%. clang++ could not compile the karma test, so that has been omitted.
| clang++ 1.1 (trunk 97850), Boost 1.42 | Percentage |
|---|---|
| sprintf char[] (reuse string) | 100.00% |
| sprintf char[] (new string) | 129.02% |
| snprintf char[] (reuse string) | 99.57% |
| sprintf &string[0] (reuse string) | 102.61% |
| snprintf &string[0] (reuse string) | 103.92% |
| stringstream (new stream, reuse string) | 493.95% |
| stringstream (new stream, new string) | 499.09% |
| stringstream (reuse stream, reuse string) | 140.36% |
| stringstream (reuse stream, new string) | 133.43% |
| strstream (new stream, reuse string) | 506.13% |
| strstream (reuse stream, reuse string) | 279.85% |
| lexical_cast (reuse string) | 101.45% |
| lexical_cast (new string) | 95.87% |
C++ 02 Feb 2010 16:38:56
C++ Find Unique Elements Speed
A performance benchmark of which method is faster of finding all unique elements of a vector. The goal is having an ordered container with just the unique elements, where the tested methods are std::sort()+std::unique()+erase(), versus inserting into a separate std::set, versus inserting into std::unordered_set and then into std::set, and finally inserting into boost::unordered_set and then into std::set.
Source for the integer version is speed-find-unique-int.cpp with cycle.h.
The compilers are Microsoft Visual C++ 2010 Express as VC10 with _SECURE_SCL disabled, and GNU g++ 4.3.2.
Tests were run with 10000, 100000, 1000000, and 10000000 elements, of which 1000 are unique. For my own amusement I also tried 10000 and 32767 unique elements, but relative speeds were roughly the same.
The set.insert() column describes how much time was spent in relation to the same operation with sort()+unique()+erase(). For integers, this number is above 100% for all tests, meaning it is slower to use std::set.
| <int> | sort()+unique() | set.insert() | hash+set | boost hash+set |
|---|---|---|---|---|
| VC10 n=10k | 100.00% | 273.51% | 376.30% | 97.39% |
| VC10 n=100k | 100.00% | 287.09% | 274.24% | 29.41% |
| VC10 n=1m | 100.00% | 335.19% | 329.24% | 25.60% |
| VC10 n=10m | 100.00% | 343.77% | 350.25% | 20.63% |
| - | ||||
| g++ 4.3.2 n=10k | 100.00% | 160.28% | 47.49% | 55.51% |
| g++ 4.3.2 n=100k | 100.00% | 153.75% | 22.54% | 28.49% |
| g++ 4.3.2 n=1m | 100.00% | 147.02% | 27.61% | 36.09% |
| g++ 4.3.2 n=10m | 100.00% | 138.61% | 29.63% | 35.38% |
And then there is the std::string version, source speed-find-unique-string.cpp with cycle.h.
For VC10, all is as expected from the integer results. However, for g++ it is considerably faster to use std::set.
I tried a test for g++ where I force a full string copy to get around g++’s copy-on-write implementation, but it didn’t change much.
| <string> | sort()+unique() | set.insert() | hash+set | boost hash+set |
|---|---|---|---|---|
| VC10 n=10k | 100.00% | 140.72% | 91.90% | 33.12% |
| VC10 n=100k | 100.00% | 163.90% | 106.33% | 20.82% |
| VC10 n=1m | 100.00% | 153.36% | 101.79% | 16.35% |
| VC10 n=10m | 100.00% | 158.99% | 107.24% | 16.62% |
| - | ||||
| g++ 4.3.2 n=10k | 100.00% | 62.53% | 19.44% | 22.69% |
| g++ 4.3.2 n=100k | 100.00% | 61.05% | 12.06% | 11.88% |
| g++ 4.3.2 n=1m | 100.00% | 29.23% | 5.43% | 3.99% |
| g++ 4.3.2 n=10m | 100.00% | 18.55% | 3.64% | 2.54% |
C++ 01 Feb 2010 15:32:18
Snippet: Dynamic Box of Strings
http://tinodidriksen.com/uploads/code/cpp/dynamic-string-box.cpp
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
int main() {
std::vector<std::string> strs;
strs.push_back("Hello World!");
strs.push_back("This is a text line.");
strs.push_back("Shorter line.");
size_t maxl = 0;
for (size_t i = 0 ; i<strs.size() ; ++i) {
maxl = std::max(maxl, strs[i].length());
}
std::cout << std::string(maxl+4, '*') << std::endl;
for (size_t i = 0 ; i<strs.size() ; ++i) {
std::cout << "* "
<< strs[i]
<< std::string(maxl-strs[i].length()+1, ' ')
<< "*" << std::endl;
}
std::cout << std::string(maxl+4, '*') << std::endl;
}
