shardedint.go 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. // Copyright (c) Tailscale Inc & contributors
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package syncs
  4. import (
  5. "encoding/json"
  6. "strconv"
  7. "sync/atomic"
  8. "golang.org/x/sys/cpu"
  9. )
  10. // ShardedInt provides a sharded atomic int64 value that optimizes high
  11. // frequency (Mhz range and above) writes in highly parallel workloads.
  12. // The zero value is not safe for use; use [NewShardedInt].
  13. // ShardedInt implements the expvar.Var interface.
  14. type ShardedInt struct {
  15. sv *ShardValue[intShard]
  16. }
  17. // NewShardedInt returns a new [ShardedInt].
  18. func NewShardedInt() *ShardedInt {
  19. return &ShardedInt{
  20. sv: NewShardValue[intShard](),
  21. }
  22. }
  23. // Add adds delta to the value.
  24. func (m *ShardedInt) Add(delta int64) {
  25. m.sv.One(func(v *intShard) {
  26. v.Add(delta)
  27. })
  28. }
  29. type intShard struct {
  30. atomic.Int64
  31. _ cpu.CacheLinePad // avoid false sharing of neighboring shards
  32. }
  33. // Value returns the current value.
  34. func (m *ShardedInt) Value() int64 {
  35. var v int64
  36. for s := range m.sv.All {
  37. v += s.Load()
  38. }
  39. return v
  40. }
  41. // GetDistribution returns the current value in each shard.
  42. // This is intended for observability/debugging only.
  43. func (m *ShardedInt) GetDistribution() []int64 {
  44. v := make([]int64, 0, m.sv.Len())
  45. for s := range m.sv.All {
  46. v = append(v, s.Load())
  47. }
  48. return v
  49. }
  50. // String implements the expvar.Var interface
  51. func (m *ShardedInt) String() string {
  52. v, _ := json.Marshal(m.Value())
  53. return string(v)
  54. }
  55. // AppendText implements the encoding.TextAppender interface
  56. func (m *ShardedInt) AppendText(b []byte) ([]byte, error) {
  57. return strconv.AppendInt(b, m.Value(), 10), nil
  58. }