현재 3권의 신간들인 Go Optimizations 101, Go Details & Tips 101Go Generics 101이 출간되어 있습니다. Leanpub 서점에서 번들을 모두 구입하시는 방법이 비용 대비 효율이 가장 좋습니다.

Go에 대한 많은 정보들과 Go 101 책들의 최신 소식을 얻으시려면 Go 101 트위터 계정인 @Go100and1을 팔로잉 해주세요.

Go Value Copy Costs

Value copying happens frequently in Go programming. Values assignments, argument passing and channel value send operations are all value copying involved. This article will talk about the copy costs of values of all kinds of types.

Value Sizes

The size of a value means how many bytes the direct part of the value will occupy in memory. The indirect underlying parts of a value don't contribute to the size of the value.

In Go, if the types of two values belong to the same kind, and the type kind is not string kind, interface kind, array kind and struct kind, then the sizes of the two value are always equal.

In fact, for the standard Go compiler/runtime, the sizes of two string values are also always equal. The same relation is for the sizes of two interface values.

Up to present (Go Toolchain 1.20), for the standard Go compiler (and gccgo), values of a specified type always have the same value size. So, often, we call the size of a value as the size of the type of the value.

The size of an array type depends on the element type size and the length of the array type. The array type size is the product of the size of the array element type and the array length.

The size of a struct type depends on all of the sizes and the order of its fields. For there may be some padding bytes being inserted between two adjacent struct fields to guarantee certain memory address alignment requirements of these fields, so the size of a struct type must be not smaller than (and often larger than) the sum of the respective type sizes of its fields.

The following table lists the value sizes of all kinds of types (for the standard Go compiler version 1.20). In the table, one word means one native word, which is 4 bytes on 32-bit architectures and 8 bytes on 64-bit architectures.

Kinds of Types Value Size Required by Go Specification
bool 1 byte not specified
int8, uint8 (byte) 1 byte 1 byte
int16, uint16 2 bytes 2 bytes
int32 (rune), uint32, float32 4 bytes 4 bytes
int64, uint64, float64, complex64 8 bytes 8 bytes
complex128 16 bytes 16 bytes
int, uint 1 word architecture dependent, 4 bytes on 32-bit architectures and 8 bytes on 64-bit architectures
uintptr 1 word large enough to store the uninterpreted bits of a pointer value
string 2 words not specified
pointer (safe or unsafe) 1 word not specified
slice 3 words not specified
map 1 word not specified
channel 1 word not specified
function 1 word not specified
interface 2 words not specified
struct (the sum of sizes of all fields) + (the number of padding bytes) the size of a struct type is zero if it contains no fields that have a size greater than zero
array (element value size) * (array length) the size of an array type is zero if its element type has zero size

Value Copy Costs

Generally speaking, the cost to copy a value is proportional to the size of the value. However, value sizes are not the only factor determining value copy costs. Different CPU models and compiler versions may specially optimize value copying for values with specific sizes.

In practice, we can view struct values with less than 5 fields and with sizes not larger than four native words as small-size values. The costs of copying small-size values are small.

For the standard Go compiler, except values of large-size struct and array types, other types in Go are all small-size types.

To avoid large value copy costs in argument passing and channel value send and receive operations, we should try to avoid using large-size struct and array types as function and method parameter types (including method receiver types) and channel element types. We can use pointer types whose base types are large-size types instead for such scenarios.

One the other hand, we should also consider the fact that too many pointers will increase the pressure of garbage collectors at run time. So whether large-size struct and array types or their corresponding pointer types should be used relies on specific circumstances.

Generally, in practice, we seldom use pointer types whose base types are slice types, map types, channel types, function types, string types and interface types. The costs of copying values of these assumed base types are very small.

We should also try to avoid using the two-iteration-variable forms to iterate array and slice elements if the element types are large-size types, for each element value will be copied to the second iteration variable in the iteration process.

The following is an example which benchmarks different ways to iterate slice elements.
package main

import "testing"

type S [12]int64
var sX = make([]S, 1000)
var sY = make([]S, 1000)
var sZ = make([]S, 1000)
var sumX, sumY, sumZ int64

func Benchmark_Loop(b *testing.B) {
	for i := 0; i < b.N; i++ {
		sumX = 0
		for j := 0; j < len(sX); j++ {
			sumX += sX[j][0]
		}
	}
}

func Benchmark_Range_OneIterVar(b *testing.B) {
	for i := 0; i < b.N; i++ {
		sumY = 0
		for j := range sY {
			sumY += sY[j][0]
		}
	}
}

func Benchmark_Range_TwoIterVar(b *testing.B) {
	for i := 0; i < b.N; i++ {
		sumZ = 0
		for _, v := range sZ {
			sumZ += v[0]
		}
	}
}
Run the benchmarks in the directory of the test file, we will get a result similar to:
Benchmark_Loop-4             424342 2708 ns/op
Benchmark_Range_OneIterVar-4 407905 2808 ns/op
Benchmark_Range_TwoIterVar-4 214860 5222 ns/op

We can find that the efficiency of the two-iteration-variable form is much lower than the other two. But please note that, some compilers might make special optimizations to remove the performance differences between these forms. The above benchmark result is for the standard Go compiler version 1.20.


Index↡

The Go 101 프로젝트는 Github 에서 호스팅됩니다. 오타, 문법 오류, 부정확한 표현, 설명 결함, 코드 버그, 끊어진 링크와 같은 모든 종류의 실수에 대한 수정 사항을 제출하여 Go 101을 개선을 돕는 것은 언제나 환영합니다.

주기적으로 Go에 대한 깊이 있는 정보를 얻고 싶다면 Go 101의 공식 트위터 계정인 @go100and1을 팔로우하거나 Go 101 슬랙 채널에j가입해주세요.

이 책의 디지털 버전은 아래와 같은 곳을 통해서 구매할 수 있습니다.
Go 101의 저자인 Tapir는 2016년 7월부터 Go 101 시리즈 책들을 집필하고 go101.org 웹사이트를 유지 관리하고 있습니다. 새로운 콘텐츠는 책과 웹사이트에 수시로 추가될 예정입니다. Tapir는 인디 게임 개발자이기도 합니다. Tapir의 게임을 플레이하여 Go 101을 지원할 수도 있습니다. (안드로이드와 아이폰/아이패드용):
  • Color Infection (★★★★★), 140개 이상의 단계로 이루어진 물리 기반의 캐주얼 퍼즐 게임
  • Rectangle Pushers (★★★★★), 2가지 모드와 104개 이상의 단계로 이루어진 캐주얼 퍼즐 게임
  • Let's Play With Particles, 세가지 미니 게임이 있는 캐주얼 액션 게임
페이팔을 통한 개인 기부도 환영합니다.

색인: