Thanks Peter, this is very interesting.  Indeed I would not have 
anticipated (though it makes some sense) that calling append would 
outperform the manual loop, when the prefix is big, despite the overhead of 
a builtin func call.

On Tuesday, August 22, 2017 at 7:22:42 PM UTC+2, peterGo wrote:
>
> Val,
>
> That's a lot of speculation!
>
> The original benchmark applies to the original question:
>
>    prefix := verylongstring[:3]
>
> If we change the parameters of the benchmark then we expect to get 
> different results. For example, read the Go gc compiler code. There's a 
> stack/heap optimization for 32 bytes or less.
>
> cmd/gc: allocate buffers for non-escaped strings on stack
> commit    e6fac08146df323eb95f46508bef937cdfb802fd
> https://go.googlesource.com/go/+/e6fac08146df323eb95f46508bef937cdfb802fd
> https://go-review.googlesource.com/c/go/+/3120
>
> 3 byte prefix:
>
> BenchmarkNil-4            10000000           173 ns/op          16 B/op    
>        2 allocs/op
> BenchmarkLiteral-4        10000000           171 ns/op          16 B/op    
>        2 allocs/op
> BenchmarkConvert-4        20000000            76.9 ns/op         3 B/op    
>        1 allocs/op
> BenchmarkCopy-4           20000000            67.1 ns/op         3 B/op    
>        1 allocs/op
> BenchmarkLoop-4           20000000            65.7 ns/op         3 B/op    
>        1 allocs/op
>
> 32 byte prefix:
>
> BenchmarkNil-4             5000000           235 ns/op          64 B/op    
>        2 allocs/op
> BenchmarkLiteral-4        10000000           232 ns/op          64 B/op    
>        2 allocs/op
> BenchmarkConvert-4        10000000           129 ns/op          32 B/op    
>        1 allocs/op
> BenchmarkCopy-4           10000000           118 ns/op          32 B/op    
>        1 allocs/op
> BenchmarkLoop-4           10000000           183 ns/op          32 B/op    
>        1 allocs/op
>
> 33 byte prefix:
>
> BenchmarkNil-4             5000000           295 ns/op          96 B/op    
>        2 allocs/op
> BenchmarkLiteral-4         5000000           285 ns/op          96 B/op    
>        2 allocs/op
> BenchmarkConvert-4         5000000           251 ns/op          96 B/op    
>        2 allocs/op
> BenchmarkCopy-4           10000000           143 ns/op          48 B/op    
>        1 allocs/op
> BenchmarkLoop-4           10000000           188 ns/op          48 B/op    
>        1 allocs/op
>
> 256 byte prefix:
>
> BenchmarkNil-4             2000000           661 ns/op         512 B/op    
>        2 allocs/op
> BenchmarkLiteral-4         2000000           665 ns/op         512 B/op    
>        2 allocs/op
> BenchmarkConvert-4         2000000           659 ns/op         512 B/op    
>        2 allocs/op
> BenchmarkCopy-4            5000000           369 ns/op         256 B/op    
>        1 allocs/op
> BenchmarkLoop-4            2000000           883 ns/op         256 B/op    
>        1 allocs/op
>
> $ go version
> go version devel +33484a6 Tue Aug 22 08:09:42 2017 +0000 linux/amd64
>
> $ go test -run=! -bench=. -benchmem strslice_test.go
> goos: linux
> goarch: amd64
>
> https://play.golang.org/p/tAYyecoQXY
>
> $ cat  strslice_test.go
> package main
>
> import (
>     "strings"
>     "testing"
> )
>
> const pfxLen = 3 // 3, 32, 33, 256
>
> var (
>     s      = strings.Repeat("a very, very long string", 4096)
>     prefix string
> )
>
> func BenchmarkNil(b *testing.B) {
>     for i := 0; i < b.N; i++ {
>         prefix = string(append([]byte(nil), s[:pfxLen]...))
>     }
> }
>
> func BenchmarkLiteral(b *testing.B) {
>     for i := 0; i < b.N; i++ {
>         prefix = string(append([]byte{}, s[:pfxLen]...))
>     }
> }
>
> func BenchmarkConvert(b *testing.B) {
>     for i := 0; i < b.N; i++ {
>         prefix = string([]byte(s[:pfxLen]))
>     }
> }
>
> func BenchmarkCopy(b *testing.B) {
>     for i := 0; i < b.N; i++ {
>         buffer := make([]byte, pfxLen)
>         copy(buffer, s)
>         prefix = string(buffer)
>     }
> }
>
> func BenchmarkLoop(b *testing.B) {
>     for i := 0; i < b.N; i++ {
>         buffer := make([]byte, pfxLen)
>         for i := 0; i < len(buffer); i++ {
>             buffer[i] = s[i]
>         }
>         prefix = string(buffer)
>     }
> }
> $
>
> Peter
>
>
> On Tuesday, August 22, 2017 at 8:07:01 AM UTC-4, Val wrote:
>>
>> FWIW, append is most often a small performance penalty when the number of 
>> elements is known ahead.
>> And for some reason, using built-in func copy, or an explicit loop, is 
>> slightly faster on my workstation than BenchmarkConvert.
>> Also, "small benchmarks are hard" so I suspect the small allocations in 
>> tight benchmark loops may not be representative of their real cost in a 
>> real program.
>>
>>
>> func BenchmarkCopy(b *testing.B) {
>>     for i := 0; i < b.N; i++ {
>>         buffer := make([]byte, 3)
>>         copy(buffer, s[:3])
>>         prefix = string(buffer)
>>     }
>> }
>>
>> func BenchmarkLoop(b *testing.B) {
>>     for i := 0; i < b.N; i++ {
>>         buffer := make([]byte, 3)
>>         for i := 0; i < 3; i++ {
>>             buffer[i] = s[i]
>>         }
>>         prefix = string(buffer)
>>     }
>> }
>>
>>
>> BenchmarkNil-4           30000000            61.9 ns/op          16 
>> B/op           2 allocs/op
>> BenchmarkLiteral-4       30000000            59.6 ns/op          16 
>> B/op           2 allocs/op
>> BenchmarkConvert-4       50000000            37.4 ns/op           3 
>> B/op           1 allocs/op
>> BenchmarkCopy-4          50000000            30.8 ns/op           3 
>> B/op           1 allocs/op
>> BenchmarkLoop-4          50000000            29.3 ns/op           3 
>> B/op           1 allocs/op
>>
>> Cheers
>> Val
>>
>> On Tuesday, August 22, 2017 at 1:01:36 PM UTC+2, peterGo wrote:
>>>
>>> A benchmark;
>>>
>>> https://play.golang.org/p/oUyeldDG5Q
>>>
>>> $ cat strslice_test.go
>>> package main
>>>
>>> import (
>>>     "strings"
>>>     "testing"
>>> )
>>>
>>> var (
>>>     s      = strings.Repeat("a very, very long string", 4096)
>>>     prefix string
>>> )
>>>
>>> func BenchmarkNil(b *testing.B) {
>>>     for i := 0; i < b.N; i++ {
>>>         prefix = string(append([]byte(nil), s[:3]...))
>>>     }
>>> }
>>>
>>> func BenchmarkLiteral(b *testing.B) {
>>>     for i := 0; i < b.N; i++ {
>>>         prefix = string(append([]byte{}, s[:3]...))
>>>     }
>>> }
>>>
>>> func BenchmarkConvert(b *testing.B) {
>>>     for i := 0; i < b.N; i++ {
>>>         prefix = string([]byte(s[:3]))
>>>     }
>>> }
>>>
>>> $ go test -run=! -bench=. -benchmem strslice_test.go
>>> goos: linux
>>> goarch: amd64
>>> BenchmarkNil-4           10000000           174 ns/op          16 
>>> B/op           2 allocs/op
>>> BenchmarkLiteral-4       10000000           174 ns/op          16 
>>> B/op           2 allocs/op
>>> BenchmarkConvert-4       20000000            77.0 ns/op         3 
>>> B/op           1 allocs/op
>>> PASS
>>> ok      command-line-arguments    5.512s
>>> $
>>>
>>> Peter
>>>
>>> On Tuesday, August 22, 2017 at 12:15:44 AM UTC-4, Tamás Gulácsi wrote:
>>>>
>>>> prefix := string([]byte(verylongstring[:3]))
>>>
>>>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to