syscall: optimize SlicePtrFromStrings

Instead of allocating a byte slice for every string,
calculated the required size and create
a single slice big enough to hold all of them.
As an added benefit, any error encountered
will now be returned before allocations occur.

os/exec package benchmarks:

name        old time/op    new time/op    delta
ExecEcho-8    2.14ms ± 1%    2.14ms ± 3%     ~     (p=0.842 n=10+9)

name        old alloc/op   new alloc/op   delta
ExecEcho-8    6.35kB ± 0%    6.18kB ± 0%   -2.65%  (p=0.000 n=10+10)

name        old allocs/op  new allocs/op  delta
ExecEcho-8      69.0 ± 0%      36.0 ± 0%  -47.83%  (p=0.000 n=10+10)

Change-Id: I84118d8473037d873f73903d4e4f6ed14f531ce7
Reviewed-on: https://go-review.googlesource.com/c/164961
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Josh Bleecher Snyder 2019-03-02 11:14:46 -08:00
parent beadf433c3
commit 06c86e0fc3

View file

@ -9,6 +9,7 @@
package syscall
import (
"internal/bytealg"
"runtime"
"sync"
"unsafe"
@ -81,15 +82,21 @@ func StringSlicePtr(ss []string) []*byte {
// pointers to NUL-terminated byte arrays. If any string contains
// a NUL byte, it returns (nil, EINVAL).
func SlicePtrFromStrings(ss []string) ([]*byte, error) {
var err error
bb := make([]*byte, len(ss)+1)
for i := 0; i < len(ss); i++ {
bb[i], err = BytePtrFromString(ss[i])
if err != nil {
return nil, err
n := 0
for _, s := range ss {
if bytealg.IndexByteString(s, 0) != -1 {
return nil, EINVAL
}
n += len(s) + 1 // +1 for NUL
}
bb := make([]*byte, len(ss)+1)
b := make([]byte, n)
n = 0
for i, s := range ss {
bb[i] = &b[n]
copy(b[n:], s)
n += len(s) + 1
}
bb[len(ss)] = nil
return bb, nil
}