🙆‍♀️

【Go】スライスに要素を追加する処理のベンチマークテスト比較

2024/11/22に公開

概要

スライスに要素を追加するappendインデックスを直接指定する方法のパフォーマンスが気になったので、ベンチマークテストの比較を行ってみた。

環境

  • MacBook Air M1 メモリ16GB、go 1.23.0

計測方法

  • []int型のスライスに10,000,000回要素を追加する処理のベンチマークテストを実施

サンプルコード

package main

import "testing"

const size = 10000000

// ベンチマーク関数: append の場合
func BenchmarkAppend(b *testing.B) {
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		var slice []int
		for j := 0; j < size; j++ {
			slice = append(slice, j)
		}
	}
}

// ベンチマーク関数: slice[i] = int の場合
func BenchmarkIndexAssignment(b *testing.B) {
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
        slice := make([]int, size)
        for j := 0; j < size; j++ {
            slice[j] = j
        }
    }
}

計測結果

goos: darwin
goarch: arm64
pkg: performance
cpu: Apple M1
BenchmarkAppend-8            40	  29059970 ns/op	492000713 B/op  51 allocs/op
BenchmarkIndexAssignment-8  264	   4503033 ns/op	80003085 B/op	   1 allocs/op
PASS
ok  	performance	3.679s

※ベンチマークテストでは内部で複数回の計測を実施しており、テストを数回実行しても結果のばらつきはほとんどなかったので1回分のみを記載

append(BenchmarkAppend-8)

  • 実行回数: 40回
  • 平均実行時間: 29,059,970 ns/op(約29.1ms/1回)
  • メモリ使用量: 492,000,713 B/op(約492MB/1回)
  • メモリアロケーション回数: 51回

インデックス指定(BenchmarkIndexAssignment-8)

  • 実行回数: 264回
  • 平均実行時間: 4,503,033 ns/op(約4.5ms/1回)
  • メモリ使用量: 80,003,085 B/op(約80MB/1回)
  • メモリアロケーション回数: 1回

比較

append インデックス指定
実行回数 40回 264回
平均実行時間 29,059,970 ns/op(約29.1ms/1回) 4,503,033 ns/op(約4.5ms/1回)
メモリ使用量 492,000,713 B/op(約492MB/1回) 80,003,085 B/op(約80MB/1回)
メモリアロケーション回数 51回 1回
  • 実行回数
    • インデックス指定append と比べて実行回数は 約6倍多い
  • 実行時間
    • インデックス指定append と比べて実行速度が 約6倍速い
  • メモリ使用量
    • インデックス指定append約6分の1 のメモリ使用量
  • アロケーション回数:
    • インデックス指定 は最初に要素数分のメモリを確保するので 1回 のみ

結論

  • 今回の計測方法ではインデックス指定の方が append より 実行速度、メモリ効率共に優れている結果となった
  • データ量が少ないときはそこまで影響はないかもしれないが、スライスに要素を追加する際にあらかじめサイズが分かっている場合は、インデックスを指定する方法を使用するようにしたい
GitHubで編集を提案

Discussion