A faster-looking implementation is not necessarily faster. Performance depends on the work a program does, including allocations, copying, and contention. Make code correct and clear first; optimize paths where speed or efficiency matters, and measure alternatives rather than guessing. section 8.2 section 12.0
Three common Go choices show why context matters:
- Growing an empty slice with append repeatedly allocates larger backing arrays and copies elements. If the result size is known, set the capacity before appending or set the length and assign by index. If only an upper bound is known, capacity preserves append's flexibility. section 3.5
- Repeated string += creates a new string on each iteration. strings.Builder writes to an internal buffer; Grow reserves space when the final length is known. Do not use one builder concurrently. section 5.4
- reflect.DeepEqual can compare values containing slices, but runtime reflection costs more than ==. For performance-sensitive runtime comparisons, consider field-by-field equality; first check for an existing standard-library comparison function. section 3.13
Measure the slice conversion
With one million input elements, benchmarks reported 49,739,882 ns/op for an initially empty slice, 13,438,544 ns/op with capacity reserved, and 12,800,411 ns/op with length set. The direct-index version was slightly faster, but both sized versions avoided repeated backing-array growth. section 3.5
func concat(values []string) string {
total := 0
for i := 0; i < len(values); i++ { #1
total += len(values[i])
}
sb := strings.Builder{}
sb.Grow(total) #2
for _, value := range values {
_, _ = sb.WriteString(value)
}
return sb.String()
}
This concatenation implementation totals the input lengths before calling Grow, making the reservation explicit. section 5.4
Do not add concurrency merely to make a program faster. Its benefit depends on what can run in parallel and how much contention the work creates. section 8.2







































