转发: strconv has very bad performance

1,397 views
Skip to first unread message

qinhui99

unread,
Jun 10, 2012, 2:39:39 AM6/10/12
to golang-nuts
Sorry, I don't know how to contact the Go development team. Is it the right way to post the issue to them?
 

qinhui99
 
发件人: hui qin
发送时间: 2012-06-10 10:45
收件人: dsymonds
主题: strconv has very bad performance
hi,

I found a very bad performance for strconv of Go.  I did some tests between Go and JAVA for string function. 

1.the first test:

Loop 1 million times for translating  int32  i  into string , then I got the results as follow,

for i:=1;i<=count;i++{

        strconv.Itoa(i)

    }

Go versionuse: strconv.Itoa(i)

time costs(ns):328125000

time costs(ns)328 per operation

 

JAVA versionuse: ””+i

time costs(ns):111984107

time costs(ns):111 per operation

 

2. the second test:

Loop 1 million times for translating  float32  1.0  into string , then I got the results as follow,

for i:=1;i<=count;i++{

        strconv.FormatFloat(1.0,'e',10,32)

    }

Go versionuse:strconv.FormatFloat(1.0,'e',10,32)

time costs(ns):1093750000

time costs(ns)1093 per operation

 

JAVA versionuse:””+1.0

time costs(ns):20155200

time costs(ns):20 per operation

 

As you see , Go has a very bad performance comparing  JAVA.  The strconv function is very bad performance and needed to do some refactoring. Since the   strconv function is very important basic stone of Go , so I need your help. Thanks.

Hui Qin

Ian Lance Taylor

unread,
Jun 10, 2012, 1:49:05 PM6/10/12
to qinhui99, golang-nuts
qinhui99 <qinh...@gmail.com> writes:

> 2. the second test:
> Loop 1 million times for translating float32 1.0 into string , then I got the results as follow,
> for i:=1;i<=count;i++{
> strconv.FormatFloat(1.0,'e',10,32)
> }
> Go version(use:strconv.FormatFloat(1.0,'e',10,32))
> time costs(ns):1093750000
> time costs(ns)1093 per operation
>
> JAVA version(use:””+1.0)
> time costs(ns):20155200
> time costs(ns):20 per operation

Your Java number looks physically impossible. I conclude that your Java
program has hoisted the call out of the loop. That is a good
optimization, but it is a compiler optimization, not an issue with
strconv.

If you want to measure strconv performance, you need to write a loop
with a value that can not be hoisted. E.g., call
strconv.FormatFloat(float64(i), 'e', 10, 32)

Or use the benchmarks that already exist in the strconv package, in
itoa_test.go and ftoa_test.go. To run these benchmarks, go to
src/pkg/strconv in your Go sources and run

go test -test.bench='.*'

You'll have to convert them to Java yourself, though.

Ian

qinhui99

unread,
Jun 11, 2012, 7:19:52 AM6/11/12
to Ian Lance Taylor, golang-nuts
hi Ian,
 
After testing again according to your requirements, the result is the same. The performance of strconv is very bad comparing to Java.
The test result as follows,
 
1. float32 to string

Go version: (GO 1.0.1, winxp)

go test ftoa_test.go -bench="."

BenchmarkFloat32ToStr     100000             12812 ns/op

ok      command-line-arguments  1.938s 
 
Java version: (JDK1.7 ,winxp , use the "-server" jvm parameter)

Loop 10000000 times

Time costs 2.45952435 (s).

performance:2459 ns/op

 

2.  int32 to string

Go version: (GO 1.0.1, winxp)

go test  itoa_test.go -bench="."

BenchmarkFormatIntByQH    500000              2843 ns/op

ok      command-line-arguments  1.797s

Java version: (JDK1.7 ,winxp , use the "-server" jvm parameter)

Loop 10000000 times

Time costs 0.695598031 (s).

performance:695 ns/op

 

As you can see,compare to JDK1.7, Go is very very slowly.  But I did another test for looping some static string likes these:

for i:=1;i<=count;i++{

        s="hello"
    }
 
Then Go is really faster than JDK1.7. So ,according to my these tests, I think the strconv tools should be refactored 
in order to get  better performance.  Pls let me know your ideas, thanks.
 
hui qin
 

qinhui99
 
发件人: Ian Lance Taylor
发送时间: 2012-06-11 01:49
收件人: qinhui99
抄送: golang-nuts
主题: Re: [go-nuts]转发: strconv has very bad performance
itoa_test.go
ftoa_test.go
StringBenchmarkTest.java
AtoStringBenchmarkTest.java
B.java
BenchmarkTest.java

unread,
Jun 11, 2012, 8:24:59 AM6/11/12
to golan...@googlegroups.com, Ian Lance Taylor, qinhui99
If you are after absolute performance, you should use C or C++:

Go (32-bit):   520ns
Java7 -client:   314ns   (using: ""+i)
gcc -O2:   68ns
g++ -O2:   38ns

Java ""+i is slower than C++ by a factor of 10. Taking this into consideration, the claim that Go has "very bad performance" compared to Java seems pointless.
itoa.c
itoa.cc

matt

unread,
Jun 11, 2012, 9:19:31 AM6/11/12
to golan...@googlegroups.com, qinhui99
Just my take but wouldn't a more fairer comparison on the float convert side be:

GO:

package main                                                                                                        
                                                                                                                    
import (                                                                                                            
    "fmt"                                                                                                           
    "strconv"                                                                                                       
)                                                                                                                                                                                                                                      
                                                                                                                    
func main () {                                                                                                      
    for i := 0; i < 1000000; i++ {                                                                                  
        fmt.Println(strconv.FormatFloat(float64(i), 'e', 10, 32))                                                   
    }                                                                                                               
}

JAVA:

class StringConvert {                                                                                               
    public static void main(String[] args) {                                                                        
        for (int i = 0; i < 1000000; i++) {                                                                         
            System.out.println(String.format("%1$10e", (float)i));                                                  
        }                                                                                                           
    }                                                                                                               

Actually in this case, using the above "fairer" tests, on my machine the results are much closer, go is within 30% of the Java code.

$> time java StringConvert
real 0m26.612s
user 0m8.290s
sys 0m3.700s

$> time java -server StringConvert
real 0m26.644s
user 0m8.860s
sys 0m3.410s

$> time go run strconvtest.go
real 0m34.228s
user 0m2.530s
sys 0m2.160s

Given the maturity of the compiler, and the unrealistic nature of this benchmark 30% really isn't "very slow". Further if you want to dig into the performance you can just extend the go version with runtime/pprof. Which shows us the following:

(pprof) top20
Total: 489 samples
     269  55.0%  55.0%      273  55.8% syscall.Syscall
      17   3.5%  58.5%       19   3.9% itab
      16   3.3%  61.8%       17   3.5% strconv.(*decimal).Assign
      14   2.9%  64.6%       22   4.5% strconv.fmtE
      11   2.2%  66.9%       59  12.1% strconv.genericFtoa
      10   2.0%  68.9%       31   6.3% runtime.makeslice
      10   2.0%  71.0%       34   7.0% runtime.mallocgc
      10   2.0%  73.0%       10   2.0% runtime.memmove
      10   2.0%  75.1%       10   2.0% runtime.rnd
       9   1.8%  76.9%       10   2.0% strconv.rightShift
       7   1.4%  78.3%       36   7.4% fmt.(*pp).printField
       7   1.4%  79.8%      288  58.9% os.(*File).write
       4   0.8%  80.6%        9   1.8% fmt.(*cache).put
       4   0.8%  81.4%      362  74.0% fmt.Println
       4   0.8%  82.2%       14   2.9% runtime.MCache_Alloc
       4   0.8%  83.0%        8   1.6% runtime.appendslice
       3   0.6%  83.6%        5   1.0% MCentral_Alloc
       3   0.6%  84.3%        9   1.8% fmt.(*fmt).padString
       3   0.6%  84.9%       39   8.0% fmt.(*pp).doPrint
       3   0.6%  85.5%        9   1.8% gostringsize
(pprof) top20 -cum
Total: 489 samples
       3   0.6%   0.6%      478  97.8% main.main
       0   0.0%   0.6%      478  97.8% runtime.main
       0   0.0%   0.6%      478  97.8% schedunlock
       4   0.8%   1.4%      362  74.0% fmt.Println
       2   0.4%   1.8%      352  72.0% fmt.Fprintln
       3   0.6%   2.5%      293  59.9% os.(*File).Write
       7   1.4%   3.9%      288  58.9% os.(*File).write
       1   0.2%   4.1%      274  56.0% syscall.Write
     269  55.0%  59.1%      273  55.8% syscall.Syscall
       2   0.4%  59.5%      103  21.1% strconv.FormatFloat
      11   2.2%  61.8%       59  12.1% strconv.genericFtoa
       3   0.6%  62.4%       39   8.0% fmt.(*pp).doPrint
       7   1.4%  63.8%       36   7.4% fmt.(*pp).printField
      10   2.0%  65.8%       34   7.0% runtime.mallocgc
      10   2.0%  67.9%       31   6.3% runtime.makeslice
      14   2.9%  70.8%       22   4.5% strconv.fmtE
       1   0.2%  71.0%       21   4.3% makeslice1
      17   3.5%  74.4%       19   3.9% itab
       1   0.2%  74.6%       17   3.5% fmt.(*pp).handleMethods
      16   3.3%  77.9%       17   3.5% strconv.(*decimal).Assign

qinhui99

unread,
Jun 11, 2012, 10:00:14 AM6/11/12
to golang-nuts, Ian Lance Taylor
No, I don't need a C or C++ solution. I invest Go too much ,so I just need a Go solution.  I try to use map to cache the int32 string ,but it works little. As you know,since the databases always have the int32 and float32 datas.When you need to show the datas to the customers, you need to translate the int32 and float32 into string.Then you need to use the strconv functions .If it is not so good,would you like to improve it ? So,  is there any suggestion to improve the performance of  strconv ?
 

hui qin
 
发件人: 
发送时间: 2012-06-11 20:24
收件人: golang-nuts
主题: Re: Re: [go-nuts]转发: strconv has very bad performance

chris dollin

unread,
Jun 11, 2012, 10:21:13 AM6/11/12
to qinhui99, golang-nuts, Ian Lance Taylor
On 11 June 2012 15:00, qinhui99 <qinh...@gmail.com> wrote:
> No, I don't need a C or C++ solution. I invest Go too much ,so I just
> need a Go solution.  I try to use map to cache the int32 string ,but it
> works little. As you know,since the databases always have the int32 and
> float32 datas.When you need to show the datas to the customers, you need to
> translate the int32 and float32 into string.Then you need to use the strconv

(or the fmt package)

> functions .If it is not so good,would you like to improve it ?

Do you have reason to believe that it will be too slow /in the
appication you have in mind/?

Because unless I've mis-read your numbers, your Go test
does 100_000 conversions in 1.2sec. Showing that many
numbers /as numbers/ to a customer is likely to take a lot
more than 1.2 secs anyway.

[I'm not saying Go's convert-float-to-string couldn't or shouldn't
be faster, just that your example is not compelling to me.]

Chris

--
Chris "allusive" Dollin

Jim Whitehead II

unread,
Jun 11, 2012, 10:23:12 AM6/11/12
to chris dollin, qinhui99, golang-nuts, Ian Lance Taylor
Indeed, if we accept the premise that the strconv family of functions
you are trying to use are slower than they are in Java.. can you
actually show that this has a measurable impact on the performance of
your application? If you're displaying these strings to the user, I
would expect the I/O or updating a graphical display would absolutely
dwarf the performance loss you are seeing here.

- Jim

N. Riesco - GMail

unread,
Jun 11, 2012, 11:59:09 AM6/11/12
to matt, golan...@googlegroups.com, qinhui99
Out of curiosity, I've dug into the profile results a little bit more with:
------------
package main

import (
"os"
"runtime/pprof"
"strconv"
)

func main() {
f, _ := os.Create("strconv3.prof")
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
for i := 0; i < 10000000; i++ {
strconv.FormatFloat(float64(i), 'e', 10, 32)
}
}
-----------------
(pprof) top20
Total: 551 samples
136 24.7% 24.7% 297 53.9% strconv.genericFtoa
48 8.7% 33.4% 72 13.1% strconv.fmtE
44 8.0% 41.4% 49 8.9% strconv.(*decimal).Assign
37 6.7% 48.1% 38 6.9% strconv.rightShift
29 5.3% 53.4% 29 5.3% runtime.memmove
28 5.1% 58.4% 164 29.8% runtime.mallocgc
25 4.5% 63.0% 63 11.4% sweep
14 2.5% 65.5% 14 2.5% runtime.MHeap_Lookup
14 2.5% 68.1% 512 92.9% strconv.FormatFloat
13 2.4% 70.4% 18 3.3% MCentral_Alloc
13 2.4% 72.8% 39 7.1% runtime.MCache_Free
11 2.0% 74.8% 523 94.9% main.main
11 2.0% 76.8% 11 2.0% runtime.markallocated
11 2.0% 78.8% 11 2.0% scanblock
10 1.8% 80.6% 46 8.3% runtime.MCache_Alloc
10 1.8% 82.4% 18 3.3% runtime.appendslice
8 1.5% 83.8% 94 17.1% runtime.makeslice
7 1.3% 85.1% 88 16.0% gostringsize
7 1.3% 86.4% 7 1.3% runtime.xchg
6 1.1% 87.5% 15 2.7% MCentral_Free
---------------
(pprof) list strconv.genericFtoa
Total: 551 samples
ROUTINE ====================== strconv.genericFtoa in
/usr/lib/go/src/pkg/strconv/ftoa.go
136 297 Total samples (flat / cumulative)
[...]
106 106 104: d := new(decimal)
[...]
-----------------


and looking into the definition of decimal, one can explain why the line
above is the most expensive in the profile.
--------------
type decimal struct {
d [800]byte // digits
nd int // number of digits used
dp int // decimal point
neg bool
trunc bool // discarded nonzero digits beyond d[:nd]
}
-------------------


without digging much further and since decimal is used in multiple
places in strconv, one can guess that optimizing this piece of code
might not be trivial:
-----------------
Local declarations and uses
package strconv
/src/pkg/strconv/atof.go
use 55 148 248 259 271 297 328 364 386
/src/pkg/strconv/decimal.go
use 22 71 81 109 232 278 298 317 329 338 362
/src/pkg/strconv/extfloat.go
use 270 389 478
/src/pkg/strconv/ftoa.go
use 104 188 218 237 287 348
/src/pkg/strconv/internal_test.go
use 9 10
-----------------


Nico

Rémy Oudompheng

unread,
Jun 11, 2012, 1:48:08 PM6/11/12
to N. Riesco - GMail, matt, golan...@googlegroups.com, qinhui99
The printing of float32 is not optimized like the printing of
float64s. I never use it and there is a need for thorough tests.
It should not be very difficult to adapt the existing implementation
of Grisu algorithm to support a 32-bit precision. It is mostly a job
of eliminating some hardcoded assumptions. A large speedup is probably
expected.

Rémy.

qinhui99

unread,
Jun 11, 2012, 9:03:42 PM6/11/12
to N. Riesco - GMail, golang-nuts
Thanks. In china, we can not visit many oversea websites through the normal way for the government's control. So I do not know the Go team whether accept this issue. But in my opinion, the jungle law still works. In CPU market, INTEL is the leader,AMD is the follower.If AMD says that since INTEL is too excellent to catch up with,so let AMD give up. Then what's the future of AMD?  In the open source languages market, JAVA is the leader,Go is the follower. If Go has no any courage to challenge JAVA, then what's the future of Go?  I don't know the future, I just know that just the winner will be the survivor.
 

qinhui99

Dave Cheney

unread,
Jun 11, 2012, 9:15:00 PM6/11/12
to qinhui99, N. Riesco - GMail, golang-nuts
Hello,

Rémy has pointed out that float32 string conversion may not be as
optimised as float64. This is possibly because float64 is the default
type for floating point literals.

What are the results of your benchmarking if you redo your tests as
float64's. If there is a concrete difference here then I'm sure it
will spur people to address it.

Cheers

Dave

Daniel Theophanes

unread,
Jun 11, 2012, 11:13:20 PM6/11/12
to golan...@googlegroups.com, N. Riesco - GMail, qinhui99
ARM processors dominate cell phones due to their power properties. People who by AMD might be interested in not paying a premium and getting more physical cores. Many businesses run on the .NET stack, others the Java stack. I'm not going to use Go for everything.

Go is a tool and is very useful to me even though I don't use it every time I sit down to program. Java may be the best tool for the job your doing right now and that's great! I don't think Go is trying to challenge anything; it is just trying to be a really useful tool.


On Monday, June 11, 2012 6:03:42 PM UTC-7, Tom Qin wrote:


Ian Lance Taylor

unread,
Jun 12, 2012, 1:22:21 AM6/12/12
to qinhui99, N. Riesco - GMail, golang-nuts
qinhui99 <qinh...@gmail.com> writes:

> Thanks. In china, we can not visit many oversea websites through the
> normal way for the government's control. So I do not know the Go team
> whether accept this issue. But in my opinion, the jungle law still
> works. In CPU market, INTEL is the leader,AMD is the follower.If AMD
> says that since INTEL is too excellent to catch up with,so let AMD
> give up. Then what's the future of AMD? In the open source languages
> market, JAVA is the leader,Go is the follower. If Go has no any
> courage to challenge JAVA, then what's the future of Go? I don't know
> the future, I just know that just the winner will be the survivor.

Go and Java are very different languages. Use the one that is best for
your job. If the most important characteristic of your program is how
fast it can convert a 32-bit floating point number to a string, then it
looks like right now you should use Java.

In the meantime please consider opening an issue for Go to look into
speeding up the floating point conversion routines. Thanks.

Ian

qinhui99

unread,
Jun 12, 2012, 2:30:01 AM6/12/12
to Ian Lance Taylor, golang-nuts
Thanks, I report this issue to the Go teem.The issue link is http://code.google.com/p/go/issues/detail?id=3725&thanks=3725&ts=1339481654 .
 
 
 

qinhui99
 
Date: 2012-06-12 13:22
Subject: Re: [go-nuts] Re: strconv has very bad performance

unread,
Jun 12, 2012, 3:24:43 AM6/12/12
to golan...@googlegroups.com, N. Riesco - GMail, qinhui99
CPUs are unlike programming languages: an Intel x86 CPU cannot be used as a component of an AMD x86 CPU, but a function in Go can be (theoretically) called from Java and a Java method can be (in theory) called from Go. Interoperability is possible in the case of programming languages.

The future of Go and Java is peaceful.

It seems an incorrect prediction to think that the winning programming language can destroy the loosing programming language.

On Tuesday, June 12, 2012 3:03:42 AM UTC+2, Tom Qin wrote:

Thanks. In china, we can not visit many oversea websites through the normal way for the government's control. So I do not know the Go team whether accept this issue. But in my opinion, the jungle law still works. In CPU market, INTEL is the leader,AMD is the follower.If AMD says that since INTEL is too excellent to catch up with,so let AMD give up. Then what's the future of AMD?
I believe the distant future of CPUs will be akin to open source development: people over the world will be able to improve the design of an AMD CPU or an Intel CPU over Internet. The design of an x86 CPU and an emulator/simulator of the design will be stored in a public repository (git, mercurial, or something like that). AMD and Intel will choose the best design and manufacture it.

qinhui99

unread,
Jun 12, 2012, 6:53:43 AM6/12/12
to N. Riesco - GMail, golang-nuts
I don't mean that Go will die. I just want to say , if the Go team does not work hard, it will become common and lose the color. And maybe two or three years later ,another more perfect language will occupy the position of Go. No doubt to say, I like Go. When I say it is not good, I just mean that Go should be able to  become more perfect.
 
As far as to the function which Go call Java or Java call Go, I love it. I am looking for this function. Do you have any suggestion?
 

qinhui99

N. Riesco - GMail

unread,
Jun 12, 2012, 7:04:08 AM6/12/12
to qinhui99, golang-nuts
I would also consider Remy's suggestion.
You can access his code for float64 in:

http://groups.google.com/group/golang-nuts/browse_thread/thread/a41264a78e4ad804

Nico

Steph D

unread,
Jun 12, 2012, 5:17:23 AM6/12/12
to golang-nuts
On 12 Jun., 07:22, Ian Lance Taylor <i...@google.com> wrote:

> Go and Java are very different languages.  Use the one that is best for
> your job.  If the most important characteristic of your program is how
> fast it can convert a 32-bit floating point number to a string, then it
> looks like right now you should use Java.
>
> In the meantime please consider opening an issue for Go to look into
> speeding up the floating point conversion routines.  Thanks.
>
> Ian

That seems like a very reasonable way of handling the issue to me. I
think it would also be important to know if this is also an issue
using 64bit Floats.

Just a thought or two in addition:
It think it might be helpfull to create a list of low hanging fruit
for optimizations. I am sure that sooner or later people will show up
and start picking off things on that list and just do them.

Raw speed continues to be the most important selling argument
everywhere, and it always helps to be faster.


Kyle Lemons

unread,
Jun 12, 2012, 4:20:55 PM6/12/12
to qinhui99, N. Riesco - GMail, golang-nuts
On Tue, Jun 12, 2012 at 3:53 AM, qinhui99 <qinh...@gmail.com> wrote:
I don't mean that Go will die. I just want to say , if the Go team does not work hard, it will become common and lose the color.
Make no mistake: the Go team works incredibly hard.  They are, however, a very small team with very big fish to fry.  They accept (and encourage!) contributions from the open source community to make improvements, and many significant performance enhancements (the math/big package for one, if I recall) have come from outside the Go team.  Having found one very specific (and, I would argue, relatively low-priority) performance difference (though I'm not yet convinced that its quite as big as you think) doesn't strike me as sufficient evidence to extrapolate any sort of eventuality, let alone Go "becom[ing] common and los[ing] the color."
And maybe two or three years later ,another more perfect language will occupy the position of Go.
As was stated in the recent thread about the TIOBE index, the Go team and much of the community is more concerned about making a language that is useful to them and can solve real problems and less concerned about occupying some sort of "position."  If the broader programming language community wants to recognize Go, cool, but otherwise we'll keep using it to make great stuff and to spread the joy to coworkers and friends. 
No doubt to say, I like Go. When I say it is not good, I just mean that Go should be able to  become more perfect.
You need look no further than the repository checkins to see that Go is improving daily (often even hourly or faster).  In relative terms (C: 1970s, C++: 1980s, Java: 1990s, C#: 2000s... Go: 2009) the language is pretty young, arguably in its infancy.
As far as to the function which Go call Java or Java call Go, I love it. I am looking for this function. Do you have any suggestion?
There's nothing in the standard library.  I think there have been whispers and mumblings of a JNI interface, but I don't know their status.

N. Riesco - GMail account

unread,
Jun 13, 2012, 11:35:19 AM6/13/12
to qinhui99, golan...@googlegroups.com
My understanding of Remy's post (quoted below) is that Go is using the
Grisu algorithm to convert a float64 into a string, and an unoptimised
algorithm to convert float32. Since in Java floats are float32, a
comparison between java and go is actually comparing with go's
unoptimised algorithm for float32.
What do you need for your application? Do you need to convert a float64
or a float32? If a float64 is ok with your application, then you can use
go without any changes. If you cannot avoid the use of float32, then
Remy suggested his code could be adapted to convert a float32.


Nico


PS: Regarding the benchmarks, I'm getting similar results. In my machine
and using go version weekly.2012-03-27 +64b63f65303a, Remy's file is
20-30% faster than go, except for the last benchmark:

$ go test -bench="BenchmarkAppendFloat*"
PASS
BenchmarkAppendFloatDecimal 5000000 603 ns/op
BenchmarkAppendFloat 5000000 627 ns/op
BenchmarkAppendFloatExp 5000000 619 ns/op
BenchmarkAppendFloatNegExp 5000000 610 ns/op
BenchmarkAppendFloatBig 2000000 820 ns/op
ok strconv 18.863s

$ go test -bench=".*" grisu.go grisu_test.go
PASS
BenchmarkAppendGrisu1Decimal 5000000 718 ns/op
BenchmarkAppendGrisu1 2000000 795 ns/op
BenchmarkAppendGrisu1Exp 2000000 750 ns/op
BenchmarkAppendGrisu1NegExp 2000000 803 ns/op
BenchmarkAppendGrisu1Big 2000000 803 ns/op
ok command-line-arguments 14.218s

I think this results might be interesting to others. I hope you don't
mind me taking the conversation back to the mailing list.
Reply all
Reply to author
Forward
0 new messages