Skip to content

Commit db73f21

Browse files
committed
chore: add hashtriemap implementation
This PR adds a hashtriemap implementation to the project. This is mostly a direct copy from unique package [^1] internals [^2] with some adjustments to make it work with our project. Once `maphash.Comparable` lands we can remove dependency on Go internals [^3]. Michael Knyszek says he plans to use this implementation as a general idea for the future generic sync/v2.Map, but nothing is in stone yet. [^1]: golang/go#62483 [^2]: https://go-review.googlesource.com/c/go/+/573956 [^3]: golang/go#54670 Signed-off-by: Dmitriy Matrenichev <[email protected]>
1 parent 8485864 commit db73f21

10 files changed

+1103
-20
lines changed

.dockerignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
22
#
3-
# Generated on 2024-05-19T20:59:37Z by kres dccd292.
3+
# Generated on 2024-05-27T12:37:27Z by kres b5844f8.
44

55
*
66
!channel
7+
!concurrent
78
!containers
89
!ensure
910
!maps

.github/workflows/ci.yaml

+27-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
22
#
3-
# Generated on 2024-05-19T18:27:53Z by kres dccd292.
3+
# Generated on 2024-05-27T12:37:27Z by kres b5844f8.
44

55
name: default
66
concurrency:
@@ -29,16 +29,32 @@ jobs:
2929
- self-hosted
3030
- generic
3131
if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/'))
32-
services:
33-
buildkitd:
34-
image: moby/buildkit:v0.13.2
35-
options: --privileged
36-
ports:
37-
- 1234:1234
38-
volumes:
39-
- /var/lib/buildkit/${{ github.repository }}:/var/lib/buildkit
40-
- /usr/etc/buildkit/buildkitd.toml:/etc/buildkit/buildkitd.toml
4132
steps:
33+
- name: gather-system-info
34+
id: system-info
35+
uses: kenchan0130/[email protected]
36+
continue-on-error: true
37+
- name: print-system-info
38+
run: |
39+
MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024))
40+
41+
OUTPUTS=(
42+
"CPU Core: ${{ steps.system-info.outputs.cpu-core }}"
43+
"CPU Model: ${{ steps.system-info.outputs.cpu-model }}"
44+
"Hostname: ${{ steps.system-info.outputs.hostname }}"
45+
"NodeName: ${NODE_NAME}"
46+
"Kernel release: ${{ steps.system-info.outputs.kernel-release }}"
47+
"Kernel version: ${{ steps.system-info.outputs.kernel-version }}"
48+
"Name: ${{ steps.system-info.outputs.name }}"
49+
"Platform: ${{ steps.system-info.outputs.platform }}"
50+
"Release: ${{ steps.system-info.outputs.release }}"
51+
"Total memory: ${MEMORY_GB} GB"
52+
)
53+
54+
for OUTPUT in "${OUTPUTS[@]}";do
55+
echo "${OUTPUT}"
56+
done
57+
continue-on-error: true
4258
- name: checkout
4359
uses: actions/checkout@v4
4460
- name: Unshallow
@@ -49,7 +65,7 @@ jobs:
4965
uses: docker/setup-buildx-action@v3
5066
with:
5167
driver: remote
52-
endpoint: tcp://127.0.0.1:1234
68+
endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234
5369
timeout-minutes: 10
5470
- name: base
5571
run: |

Dockerfile

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,24 @@
22

33
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
44
#
5-
# Generated on 2024-05-19T20:59:37Z by kres dccd292.
5+
# Generated on 2024-05-27T12:37:27Z by kres b5844f8.
66

77
ARG TOOLCHAIN
88

99
# cleaned up specs and compiled versions
1010
FROM scratch AS generate
1111

1212
# runs markdownlint
13-
FROM docker.io/node:21.7.3-alpine3.19 AS lint-markdown
13+
FROM docker.io/node:22.2.0-alpine3.19 AS lint-markdown
1414
WORKDIR /src
15-
RUN npm i -g markdownlint-cli@0.39.0
15+
RUN npm i -g markdownlint-cli@0.40.0
1616
1717
COPY .markdownlint.json .
1818
COPY ./README.md ./README.md
1919
RUN markdownlint --ignore "CHANGELOG.md" --ignore "**/node_modules/**" --ignore '**/hack/chglog/**' --rules node_modules/sentences-per-line/index.js .
2020

2121
# base toolchain image
22-
FROM ${TOOLCHAIN} AS toolchain
22+
FROM --platform=${BUILDPLATFORM} ${TOOLCHAIN} AS toolchain
2323
RUN apk --update --no-cache add bash curl build-base protoc protobuf-dev
2424

2525
# build tools
@@ -53,6 +53,7 @@ RUN cd .
5353
RUN --mount=type=cache,target=/go/pkg go mod download
5454
RUN --mount=type=cache,target=/go/pkg go mod verify
5555
COPY ./channel ./channel
56+
COPY ./concurrent ./concurrent
5657
COPY ./containers ./containers
5758
COPY ./ensure ./ensure
5859
COPY ./maps ./maps

Makefile

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
22
#
3-
# Generated on 2024-05-19T18:27:53Z by kres dccd292.
3+
# Generated on 2024-05-27T12:37:27Z by kres b5844f8.
44

55
# common variables
66

@@ -17,13 +17,13 @@ WITH_RACE ?= false
1717
REGISTRY ?= ghcr.io
1818
USERNAME ?= siderolabs
1919
REGISTRY_AND_USERNAME ?= $(REGISTRY)/$(USERNAME)
20-
PROTOBUF_GO_VERSION ?= 1.33.0
20+
PROTOBUF_GO_VERSION ?= 1.34.1
2121
GRPC_GO_VERSION ?= 1.3.0
22-
GRPC_GATEWAY_VERSION ?= 2.19.1
22+
GRPC_GATEWAY_VERSION ?= 2.20.0
2323
VTPROTOBUF_VERSION ?= 0.6.0
2424
GOIMPORTS_VERSION ?= 0.21.0
2525
DEEPCOPY_VERSION ?= v0.5.6
26-
GOLANGCILINT_VERSION ?= v1.58.0
26+
GOLANGCILINT_VERSION ?= v1.58.2
2727
GOFUMPT_VERSION ?= v0.6.0
2828
GO_VERSION ?= 1.22.3
2929
GO_BUILDFLAGS ?=

concurrent/go122.go

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
// Copyright 2024 The Go Authors. All rights reserved.
6+
// Use of this source code is governed by a BSD-style
7+
// license that can be found in the LICENSE file.
8+
9+
//go:build go1.22 && !go1.24 && !nomaptypehash
10+
11+
//nolint:revive,govet,stylecheck
12+
package concurrent
13+
14+
import (
15+
"math/rand/v2"
16+
"unsafe"
17+
)
18+
19+
// NewHashTrieMap creates a new HashTrieMap for the provided key and value.
20+
func NewHashTrieMap[K, V comparable]() *HashTrieMap[K, V] {
21+
var m map[K]V
22+
23+
mapType := efaceMapOf(m)
24+
ht := &HashTrieMap[K, V]{
25+
root: newIndirectNode[K, V](nil),
26+
keyHash: mapType._type.Hasher,
27+
seed: uintptr(rand.Uint64()),
28+
}
29+
return ht
30+
}
31+
32+
// _MapType is runtime.maptype from runtime/type.go.
33+
type _MapType struct {
34+
_Type
35+
Key *_Type
36+
Elem *_Type
37+
Bucket *_Type // internal type representing a hash bucket
38+
// function for hashing keys (ptr to key, seed) -> hash
39+
Hasher func(unsafe.Pointer, uintptr) uintptr
40+
KeySize uint8 // size of key slot
41+
ValueSize uint8 // size of elem slot
42+
BucketSize uint16 // size of bucket
43+
Flags uint32
44+
}
45+
46+
// _Type is runtime._type from runtime/type.go.
47+
type _Type struct {
48+
Size_ uintptr
49+
PtrBytes uintptr // number of (prefix) bytes in the type that can contain pointers
50+
Hash uint32 // hash of type; avoids computation in hash tables
51+
TFlag uint8 // extra type information flags
52+
Align_ uint8 // alignment of variable with this type
53+
FieldAlign_ uint8 // alignment of struct field with this type
54+
Kind_ uint8 // enumeration for C
55+
// function for comparing objects of this type
56+
// (ptr to object A, ptr to object B) -> ==?
57+
Equal func(unsafe.Pointer, unsafe.Pointer) bool
58+
// GCData stores the GC type data for the garbage collector.
59+
// If the KindGCProg bit is set in kind, GCData is a GC program.
60+
// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
61+
GCData *byte
62+
Str int32 // string form
63+
PtrToThis int32 // type for pointer to this type, may be zero
64+
}
65+
66+
// efaceMap is runtime.eface from runtime/runtime2.go.
67+
type efaceMap struct {
68+
_type *_MapType
69+
data unsafe.Pointer
70+
}
71+
72+
func efaceMapOf(ep any) *efaceMap {
73+
return (*efaceMap)(unsafe.Pointer(&ep))
74+
}

concurrent/go122_bench_test.go

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
// Copyright 2024 The Go Authors. All rights reserved.
6+
// Use of this source code is governed by a BSD-style
7+
// license that can be found in the LICENSE file.
8+
9+
//go:build go1.22 && !go1.24 && !nomaptypehash
10+
11+
//nolint:wsl,testpackage
12+
package concurrent
13+
14+
import (
15+
"testing"
16+
)
17+
18+
func BenchmarkHashTrieMapLoadSmall(b *testing.B) {
19+
benchmarkHashTrieMapLoad(b, testDataSmall[:])
20+
}
21+
22+
func BenchmarkHashTrieMapLoad(b *testing.B) {
23+
benchmarkHashTrieMapLoad(b, testData[:])
24+
}
25+
26+
func BenchmarkHashTrieMapLoadLarge(b *testing.B) {
27+
benchmarkHashTrieMapLoad(b, testDataLarge[:])
28+
}
29+
30+
func benchmarkHashTrieMapLoad(b *testing.B, data []string) {
31+
b.ReportAllocs()
32+
m := NewHashTrieMap[string, int]()
33+
for i := range data {
34+
m.LoadOrStore(data[i], i)
35+
}
36+
b.ResetTimer()
37+
b.RunParallel(func(pb *testing.PB) {
38+
i := 0
39+
for pb.Next() {
40+
_, _ = m.Load(data[i])
41+
i++
42+
if i >= len(data) {
43+
i = 0
44+
}
45+
}
46+
})
47+
}
48+
49+
func BenchmarkHashTrieMapLoadOrStore(b *testing.B) {
50+
benchmarkHashTrieMapLoadOrStore(b, testData[:])
51+
}
52+
53+
func BenchmarkHashTrieMapLoadOrStoreLarge(b *testing.B) {
54+
benchmarkHashTrieMapLoadOrStore(b, testDataLarge[:])
55+
}
56+
57+
func benchmarkHashTrieMapLoadOrStore(b *testing.B, data []string) {
58+
b.ReportAllocs()
59+
m := NewHashTrieMap[string, int]()
60+
61+
b.RunParallel(func(pb *testing.PB) {
62+
i := 0
63+
for pb.Next() {
64+
_, _ = m.LoadOrStore(data[i], i)
65+
i++
66+
if i >= len(data) {
67+
i = 0
68+
}
69+
}
70+
})
71+
}

concurrent/go122_test.go

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
// Copyright 2024 The Go Authors. All rights reserved.
6+
// Use of this source code is governed by a BSD-style
7+
// license that can be found in the LICENSE file.
8+
9+
//go:build go1.22 && !go1.24 && !nomaptypehash
10+
11+
//nolint:revive,nlreturn,wsl,gocyclo,unparam,unused,cyclop,testpackage
12+
package concurrent
13+
14+
import (
15+
"testing"
16+
"unsafe"
17+
)
18+
19+
func TestHashTrieMap(t *testing.T) {
20+
testHashTrieMap(t, func() *HashTrieMap[string, int] {
21+
return NewHashTrieMap[string, int]()
22+
})
23+
}
24+
25+
func TestHashTrieMapBadHash(t *testing.T) {
26+
testHashTrieMap(t, func() *HashTrieMap[string, int] {
27+
// Stub out the good hash function with a terrible one.
28+
// Everything should still work as expected.
29+
m := NewHashTrieMap[string, int]()
30+
m.keyHash = func(_ unsafe.Pointer, _ uintptr) uintptr {
31+
return 0
32+
}
33+
return m
34+
})
35+
}

0 commit comments

Comments
 (0)