В Go 1.23 добавили новый пакет unique для “interning”. Interning - это процесс хранения в памяти только одной копии значения и совместного использования уникальной ссылки на нее вместо аллокации нескольких копий и траты памяти.

Подробней можно посмотреть в Реализация interning в Go

Interning позволяет:

  • уменьшить потребление памяти
  • не тратить время на лишние аллокации и освобождение памяти
  • увеличить скорость сравнения объектов

Посмотрим насколько эффективней может стать сравнение строк

Будем сравнивать две строки разных размеров.

Small - строки увеличены в 10 раз, Medium - в 100 и Large - в 1000000.

StringCompare - обычное сравнение двух строк, Canonicalising - использует unique.

package main

import (
	"strings"
	"testing"
	"unique"
)

func BenchmarkStringCompareSmall(b *testing.B)  { benchStringComparison(b, 10) }
func BenchmarkStringCompareMedium(b *testing.B) { benchStringComparison(b, 100) }
func BenchmarkStringCompareLarge(b *testing.B)  { benchStringComparison(b, 1000000) }

func BenchmarkCanonicalisingSmall(b *testing.B)  { benchCanonicalising(b, 10) }
func BenchmarkCanonicalisingMedium(b *testing.B) { benchCanonicalising(b, 100) }
func BenchmarkCanonicalisingLarge(b *testing.B)  { benchCanonicalising(b, 1000000) }

func benchStringComparison(b *testing.B, count int) {
	s1 := strings.Repeat("2001:0db8:0001:0000:0000:0ab9:C0A8:0102,", count)
	s2 := strings.Repeat("2001:0db8:0001:0000:0000:0ab9:C0A8:0102,", count)
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		if s1 != s2 {
			b.Fatal()
		}
	}
	b.ReportAllocs()
}

func benchCanonicalising(b *testing.B, count int) {
	s1 := unique.Make(strings.Repeat("2001:0db8:0001:0000:0000:0ab9:C0A8:0102,", count))
	s2 := unique.Make(strings.Repeat("2001:0db8:0001:0000:0000:0ab9:C0A8:0102,", count))
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		if s1 != s2 {
			b.Fatal()
		}
	}
	b.ReportAllocs()
}

Получаем такие результаты:

$ go test -run='^$' -bench=.

goos: darwin
goarch: arm64
pkg: github.com/germangorelkin/sandbox/golang/interning
cpu: Apple M3 Pro

BenchmarkStringCompareSmall-12          182713568    6.398 ns/op  
BenchmarkStringCompareMedium-12         24486400     50.74 ns/op  
BenchmarkStringCompareLarge-12          1594         707434 ns/op

BenchmarkCanonicalisingSmall-12         1000000000   0.2558 ns/op 
BenchmarkCanonicalisingMedium-12        1000000000   0.2576 ns/op 
BenchmarkCanonicalisingLarge-12         1000000000   0.2497 ns/op 

PASS
ok      github.com/germangorelkin/sandbox/golang/interning      5.354s

Скорость(ns/op) сравнения у обычных строк растет отностительно их длины, а сравнения канонизированых остается неизменно низкой, независимо от того, выполняется ли сравнение над строкой длиной 10 копий или 1 000 000.


Источники


Комментарии в Telegram-группе!