comp.lang.c++@googlegroups.com | Google Groups | ![]() |
Unsure why you received this message? You previously subscribed to digests from this group, but we haven't been sending them for a while. We fixed that, but if you don't want to get these messages, send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
- Slow std integer to a string conversion (to_string/sprintf) - 15 Updates
- difference - 6 Updates
- difference - 3 Updates
- Advice from Bjarne Stroustrup - 1 Update
Luca Risolia <luca.risolia@linux-projects.org>: Nov 02 01:55AM +0100 Il 01/11/2014 23:02, JiiPee ha scritto: > my fast function please ask, I can give it then. Also, if somebody knows > faster way to convert, am ready to test it against this! I have a test > platform ready. And how can we reproduce your tests? Please post the full, compilable test program together with all the informations about the compiler, compiler version and compilation flags that you used. |
JiiPee <no@notvalid.com>: Nov 02 01:19AM On 02/11/2014 00:55, Luca Risolia wrote: > And how can we reproduce your tests? Please post the full, compilable > test program together with all the informations about the compiler, > compiler version and compilation flags that you used. I also tested intToStr that it converts correctly: first 20 million is correct. Needs to test all integers, takes 2 hours.... gonna do sometimes. compiler: GCC 4.8.1, use C++11, -O3, -s Here you go: #include <iostream> #include <ctime> inline void intToStr(unsigned val, char* c) { int size; if(val>=10000) { if(val>=10000000) { if(val>=1000000000) size=10; else if(val>=100000000) size=9; else size=8; } else { if(val>=1000000) size=7; else if(val>=100000) size=6; else size=5; } } else { if(val>=100) { if(val>=1000) size=4; else size=3; } else { if(val>=10) size=2; else if (val==0) { c[0]='0'; c[1] = '\0'; return; } else size=1; } } c += size-1; while(val>=100) { int pos = val % 100; val /= 100; *(short*)(c-1)=*(short*)(digit_pairs+2*pos); c-=2; } while(val>0) { *c--='0' + (val % 10); val /= 10; } c[size+1] = '\0'; } // speed test function: // intToStr is 132 faster than snprintf // loop 1294967295 step 60 (i+=60): intToStr 0.41 s, snprintf 54.4 s (x132), sprintf 54.2 (x131) // (max uint=4294967295) void benchmark() { clock_t begin = clock(); char str2[16]; char cU; for(unsigned int i = 0; i < 1294967295; i+=60) { // HERE COMMENT OUT THE FUNCTION YOU WANT TO TEST: //intToStr(i, str2); //snprintf (str2, 15, "%d",i); sprintf(str2,"%d",i); //itoa (i,str2,10); cU += str2[6]; // use the string so that the compiler will not optimize things out } clock_t end = clock(); double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC; std::cout<<"Time: "<<elapsed_secs<<cU<<std::endl; } |
JiiPee <no@notvalid.com>: Nov 02 01:23AM On 02/11/2014 01:19, JiiPee wrote: > // HERE COMMENT OUT THE FUNCTION YOU WANT TO TEST: obviously below that only one conversion function should be without comments... others should be in comments: like if testing snprintf then: //intToStr(i, str2); snprintf (str2, 15, "%d",i); //sprintf(str2,"%d",i); //itoa (i,str2,10); |
Jorgen Grahn <grahn+nntp@snipabacken.se>: Nov 02 02:10AM On Sat, 2014-11-01, JiiPee wrote: > also used the sixth item from the result: cU += str2[6]; inside the loop > and then printed it at the end so that the compiler would not optimize > things out). That's not a bad setup, but it's even harder than that to make benchmarks. E.g. how does this implementation affect data and code caches? Unlike in real code, the caches are probably warm when you enter the function you're benchmarking. > checking integers from it. > Not really complaining but want to start a discussion about this: why > std sometimes does not make fast functions/classes? The C++ standard has not so much to do with it; it's a problem with whatever implementation of the standard library you use. But /that/ is an interesting question: are we losing performance due to suboptimal standard library implemntations, and is it feasible to do something about it? I know, after earlier discussions here, that std::ostream implementations tend to be slower than C stdio. And I don't see that they /have/ to be. > my fast function please ask, I can give it then. Also, if somebody knows > faster way to convert, am ready to test it against this! I have a test > platform ready. /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o . |
Paavo Helde <myfirstname@osa.pri.ee>: Nov 02 02:08AM -0600 > correct. Needs to test all integers, takes 2 hours.... gonna do > sometimes. > compiler: GCC 4.8.1, use C++11, -O3, -s It appears that the crux of the fast algorithm is to have some static lookup buffer (digit_pairs) which encodes the ASCII representation of numbers 0..99. (You forgot to include that, copied it from the stackoverflow page). So, it trades some memory for speed. So far, so good. For fast operation it is essential the digit_pairs array is in the cache. In your test, this is most probably the case, but in real code the int- to-string conversions probably happen much less often, so the performance would not be so great in the real code. Standard library implementations probably operate only with data in registers so the cache access is not an issue. Another point is that in C++ you want to wrap the result in a std::string or something, and that will also slow things down. In my test runs wrapping time was in the same range than the conversion itself and if std::string happens to not use SSO then I guess it may be even much slower. So, it is not really clear how much would the win be in real programs. Anyway, here are my test results for running your test program (MSVC2012, Release x64 mode): Using _snprintf, no wrapping: 4.622 s Using intToStr, no wrapping: 1.143 s (4.0 times faster) Using _snprintf, wrapping to a std::string: 6.622 s Using boost::lexical_cast<std::string>: 8.75 s Using intToStr, wrapping to a std::string: 2.996 s (2.2 times faster than _snprintf and 2.9 times faster than boost::lexical_cast). So, here the differences are around 2-3 times and probably smaller in the real code when the lookup array is out of the cache. Not sure if this justifies optimizing something which is already quite fast anyway. OTOH, if there are indeed differences like 130 times on some platform, then I guess the standard library implementation must be quite bad indeed. Cheers Paavo |
Christian Gollwitzer <auriocus@gmx.de>: Nov 02 09:35AM +0100 Am 02.11.14 03:10, schrieb Jorgen Grahn: > I know, after earlier discussions here, that std::ostream > implementations tend to be slower than C stdio. And I don't see that > they /have/ to be. For sprintf vs. intToStr (or itoa), I'm also not surprised - sprintf has to interpret the format string at runtime, unless the compiler has a special optimization to inline sprintf. Christian |
JiiPee <no@notvalid.com>: Nov 02 10:09AM hmmm, interesting. How is it possible you have only 3 times faster and I have here constantly 130 times faster? Any idea? Did you try also with GCC? |
Jorgen Grahn <grahn+nntp@snipabacken.se>: Nov 02 12:06PM On Sun, 2014-11-02, Christian Gollwitzer wrote: >> they /have/ to be. > For sprintf vs. intToStr (or itoa), I'm also not surprised - sprintf has > to interpret the format string at runtime, Oh, I'm not surprised by /that/ -- I was only talking about iostreams above. > unless the compiler has a > special optimization to inline sprintf. I was going to say "sprintf() seems unlikely to be one of those functions", but gcc apparently has it as a builtin. No idea why though -- the documentation doesn't say. /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o . |
"Jouko Koski" <joukokoskispam101@netti.fi>: Nov 02 02:21PM +0200 "JiiPee" wrote: > I wonder how C++ standard library has not implemented a very fast > integer-to-string conversion function. They have sprintf, snprintf, > stringstream and to_string. Considering what these functions do, they may perform pretty well, actually. It is so easy to implement a function which simple, fast, and produces incorrect results. It is so obvious that 1234 should yield "1234" but what if the result, say, "१२३४" would be preferred? -- Jouko |
Paavo Helde <myfirstname@osa.pri.ee>: Nov 02 07:11AM -0600 > hmmm, interesting. How is it possible you have only 3 times faster and > I have here constantly 130 times faster? Any idea? Did you try also > with GCC? Gcc/Linux results: snprintf: 2.85 s intToStr: 0.4 s (7.1 times faster) snprintf + std::string wrap: 4.37 s boost::lexical_cast to std::string: 2.44 s intToStr + std::string wrap: 1.67 s Command-line options: g++ -O3 -Wall > g++ -v Using built-in specs. COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/usr/lib64/gcc/x86_64-suse-linux/4.6/lto-wrapper Target: x86_64-suse-linux Configured with: ../configure --prefix=/usr --infodir=/usr/share/info -- mandir=/usr/share/man --libdir=/usr/lib64 --libexecdir=/usr/lib64 -- enable-languages=c,c++,objc,fortran,obj-c++,java,ada --enable- checking=release --with-gxx-include-dir=/usr/include/c++/4.6 --enable-ssp --disable-libssp --disable-plugin --with-bugurl=http://bugs.opensuse.org/ --with-pkgversion='SUSE Linux' --disable-libgcj --disable-libmudflap -- with-slibdir=/lib64 --with-system-zlib --enable-__cxa_atexit --enable- libstdcxx-allocator=new --disable-libstdcxx-pch --enable-version- specific-runtime-libs --program-suffix=-4.6 --enable-linux-futex -- without-system-libunwind --with-arch-32=i586 --with-tune=generic -- build=x86_64-suse-linux Thread model: posix gcc version 4.6.2 (SUSE Linux) |
JiiPee <no@notvalid.com>: Nov 02 02:22PM and needs this one as well: const char digit_pairs[201] = { "00010203040506070809" "10111213141516171819" "20212223242526272829" "30313233343536373839" "40414243444546474849" "50515253545556575859" "60616263646566676869" "70717273747576777879" "80818283848586878889" "90919293949596979899" }; On 02/11/2014 01:19, JiiPee wrote: |
JiiPee <no@notvalid.com>: Nov 02 02:25PM you have the same intToStr time as I have, but I have like 40 secs for snprintf. But I have 32 bit computer, so maybe thats the reason. I ran it again, it took more than 50 secs with snprintf. Dont know.... On 02/11/2014 13:11, Paavo Helde wrote: |
Ben Bacarisse <ben.usenet@bsb.me.uk>: Nov 02 02:48PM Paavo Helde <myfirstname@osa.pri.ee> writes: <snip> >>>> wants my fast function please ask, I can give it then. Also, if >>>> somebody knows faster way to convert, am ready to test it against >>>> this! I have a test platform ready. <snip> > It appears that the crux of the fast algorithm is to have some static > lookup buffer (digit_pairs) which encodes the ASCII representation of > numbers 0..99. Worth pointing out that the code that copies the pairs is not portable for a number of reasons and, in those cases where it works, the performance may depend on the buffer alignment. I changed the code to copy the two bytes portably, and saw no significant penalty, at least in optimised code. Dragons: >> val /= 100; >> *(short*)(c-1)=*(short*)(digit_pairs+2*pos); >> c-=2; <snip> -- Ben. |
"Öö Tiib" <ootiib@hot.ee>: Nov 02 07:03AM -0800 On Sunday, 2 November 2014 16:25:53 UTC+2, JiiPee wrote: > you have the same intToStr time as I have, but I have like 40 secs for > snprintf. But I have 32 bit computer, so maybe thats the reason. No. You likely did something wrong and possibly already discovered what. Or why else you avoid posting command lines, compiler versions and system confs? Test carefully. Otherwise you just spread groundless FUD. |
"Öö Tiib" <ootiib@hot.ee>: Nov 02 07:20AM -0800 On Sunday, 2 November 2014 01:02:49 UTC+2, JiiPee wrote: > I wonder how C++ standard library has not implemented a very fast > integer-to-string conversion function. They have sprintf, snprintf, > stringstream and to_string. In my tests all of the conversions ('snprintf', 'boost::lexical_cast', 'std::to_string' etc.) are already blindingly fast. 'boost::format' for example compares most slow but is still tolerable since nothing needs to turn hundreds of millions of 'int's into readable sub second. If the string conversions affect performance then I can bet that the whole algorithm can be made faster by few orders of magnitude since something is wrong with the whole nature of it. What you do with *gigabytes* of *human-readable* ints on your 32 bit computer? |
"K' Dash" <adnanrashidpk@gmail.com>: Nov 01 04:51PM -0700 Ptr<Mac48Address> Mac people call it smart pointer. but I dont know the exact difference. Ptr is a class also, because when I click on Ptr<Mac48Address> Mac, then IDE show me this "class ptr". they mixed things with templates. :( |
Ben Bacarisse <ben.usenet@bsb.me.uk>: Nov 02 12:37AM > jacob navia <jacob@spamsink.net> writes: >>You know immediately what "Mac" is: a pointer to some class > This might be so in C. Since C does not have classes, that's not possible. > But in C++ there also are so-called »alias-declarations« > by which »Mac48Address« does not have to be class! True, but Jacob was just using information given by the op: | where Mac48Address is a class <snip> -- Ben. |
Jorgen Grahn <grahn+nntp@snipabacken.se>: Nov 02 01:50AM On Sat, 2014-11-01, K' Dash wrote: > Ptr is a class also, because when I click on Ptr<Mac48Address> Mac, > then IDE show me this "class ptr". > they mixed things with templates. :( It's like with everything else: to use it you have to read the documentation. This Ptr<T> thing seems to be a smart pointer of some kind, but it's not one of those defined by the C++ language, so exactly what it does is anyone's guess. The name doesn't exactly give any clues ... Who are "they"? BTW, it also seems a bit unusual to have a smart pointer to a "Mac48Address", which surely is an Ethernet address. These are small enough to be treated the same way as e.g. an int: copied when someone else needs them and so on. /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o . |
jacob navia <jacob@spamsink.net>: Nov 02 08:05AM +0100 Le 02/11/2014 01:21, Stefan Ram a écrit : > This might be so in C. > But in C++ there also are so-called »alias-declarations« > by which »Mac48Address« does not have to be class! Excuse, I forgot that. Thanks for confirming, C++ is unreadable. Actually you can NEVER know what you are reading since most words can be redefined at will. Luckily we don't do that in natural language. Imagine what would happen if we could do in english: using yes = no; using no = yes; and Romeo and Juliette talking in a warm summer night... :-) |
Paavo Helde <myfirstname@osa.pri.ee>: Nov 02 02:27AM -0600 jacob navia <jacob@spamsink.net> wrote in news:m34l3e$rsv$1 > Excuse, I forgot that. > Thanks for confirming, C++ is unreadable. Actually you can NEVER know > what you are reading since most words can be redefined at will. Yes, one can shoot yourself in the foot in many ways in C++ and in C, or one can choose names which are useful. "Ptr" actually contains some hint that it is a pointer. "*" on the other hand does not convey any such information, you have to know what it means. Besides, it is also possible in C to have "typedef Mac48Address* Ptr". Microsoft uses things like that a lot (LPCTSTR et. al.). Not saying this is good for anything. Cheers Paavo |
Jorgen Grahn <grahn+nntp@snipabacken.se>: Nov 02 08:40AM On Sun, 2014-11-02, Paavo Helde wrote: > Besides, it is also possible in C to have "typedef Mac48Address* Ptr". > Microsoft uses things like that a lot (LPCTSTR et. al.). Not saying this is > good for anything. The situation in C is worse, because - Typedefs are used a lot more in many subcultures (e.g. Microsoft). One API I'm using has typedef char* String; typedef const char* ConstString; and I fail to see the point of such exercises. - It is more common in C that you /do/ need to know what something really is, since the "irregular" types are so much more used: pointers, arrays, the struct hack ... /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o . |
ram@zedat.fu-berlin.de (Stefan Ram): Nov 02 12:12AM >and >Ptr<Mac48Address> Mac >where Mac48Address is a class C++ does not define a difference for such a case. In C++, one can calculate the difference of numbers or of pointers. C++ also does not define »Mac48Address« nor »Ptr«. C++ uses the name »Ptr« sometimes in examples. However, when one writes each line into a file, the diff program gives: 1c1 < Mac48Address* Mac --- |
ram@zedat.fu-berlin.de (Stefan Ram): Nov 02 12:21AM >You know immediately what "Mac" is: a pointer to some class This might be so in C. But in C++ there also are so-called »alias-declarations« by which »Mac48Address« does not have to be class! For example: #include <iostream> #include <ostream> int main( void ) { using Mac48Address = int; Mac48Address buffer; Mac48Address* Mac = &buffer; *Mac = 22; ::std::cout << *Mac << '\n'; } Above, »Mac48Address« is a alias for a type, but not for a class. |
ram@zedat.fu-berlin.de (Stefan Ram): Nov 02 12:42AM >>by which »Mac48Address« does not have to be class! >True, but Jacob was just using information given by the op: >| where Mac48Address is a class Oops. Sorry, I missed that part in the OP! |
woodbrian77@gmail.com: Nov 01 05:06PM -0700 On Saturday, November 1, 2014 5:33:58 PM UTC-5, Öö Tiib wrote: > What the 112 bytes can possibly contain? > Didn't you make something odd there (like comparing "debug" > versions, or forbidding inlining)? I used the same makefile for both builds http://webEbenezer.net/misc/makefile . The only thing that changed is that line. I'm not at my office now, but I'll check into what is in those 112 bytes later. Brian Ebenezer Enterprises http://webEbenezer.net |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment