Skip to content

metrics: use atomic.Pointer in runtimeHistogram #32361

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 7, 2025

Conversation

cuiweixie
Copy link
Contributor

No description provided.

@cskiraly
Copy link
Contributor

cskiraly commented Aug 7, 2025

Would you mind giving a meaningful description to the PR? I know it is a small change, still it is useful indication if you can describe what you change and why.

@cuiweixie
Copy link
Contributor Author

Great question! The choice between atomic.Pointer[T] and atomic.Value in Go depends on type safety, performance, and code clarity. Here's a detailed comparison:

Type Safety

atomic.Pointer[T] (Go 1.19+) provides compile-time type safety:

type User struct {
    Name string
    Age  int
}

// Type-safe pointer operations
var userPtr atomic.Pointer[User]

user := &User{Name: "Alice", Age: 30}
userPtr.Store(user)

// Compile-time type checking - no casting needed
loadedUser := userPtr.Load() // Returns *User directly
fmt.Println(loadedUser.Name)

atomic.Value requires runtime type assertions and interface{} boxing:

var userValue atomic.Value

user := &User{Name: "Alice", Age: 30}
userValue.Store(user)

// Runtime type assertion required - potential panic
loadedUser := userValue.Load().(*User) // Could panic if wrong type
fmt.Println(loadedUser.Name)

Performance Comparison

atomic.Pointer[T] is generally faster because:

  • No interface{} boxing/unboxing overhead
  • No runtime type checking
  • Direct pointer operations
// Benchmark example
func BenchmarkAtomicPointer(b *testing.B) {
    var ptr atomic.Pointer[User]
    user := &User{Name: "test"}
    
    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            ptr.Store(user)
            _ = ptr.Load()
        }
    })
}

func BenchmarkAtomicValue(b *testing.B) {
    var val atomic.Value
    user := &User{Name: "test"}
    
    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            val.Store(user)
            _ = val.Load().(*User)
        }
    })
}

When to Use Each

Use atomic.Pointer[T] when:

// 1. Working with pointers to specific types
type Config struct {
    Host string
    Port int
}

var config atomic.Pointer[Config]

// 2. Performance-critical code
type Cache struct {
    data atomic.Pointer[map[string]string]
}

func (c *Cache) Get(key string) (string, bool) {
    m := c.data.Load()
    if m == nil {
        return "", false
    }
    val, ok := (*m)[key]
    return val, ok
}

// 3. When you want compile-time type safety
func updateConfig(newConfig *Config) {
    config.Store(newConfig) // Type-safe, no casting
}

Use atomic.Value when:

// 1. Need to store different types dynamically
var dynamicValue atomic.Value

func storeAny(v interface{}) {
    dynamicValue.Store(v)
}

// 2. Working with non-pointer types
var counter atomic.Value
counter.Store(int64(42))

// 3. Legacy code or when using Go < 1.19

Migration Example

Converting from atomic.Value to atomic.Pointer[T]:

// Before (atomic.Value)
type Server struct {
    config atomic.Value // stores *Config
}

func (s *Server) UpdateConfig(cfg *Config) {
    s.config.Store(cfg)
}

func (s *Server) GetConfig() *Config {
    return s.config.Load().(*Config) // Runtime type assertion
}

// After (atomic.Pointer)
type Server struct {
    config atomic.Pointer[Config]
}

func (s *Server) UpdateConfig(cfg *Config) {
    s.config.Store(cfg)
}

func (s *Server) GetConfig() *Config {
    return s.config.Load() // No type assertion needed
}

Summary

Use atomic.Pointer[T] when:

  • ✅ You're using Go 1.19+
  • ✅ Working with pointers to specific types
  • ✅ Performance is critical
  • ✅ You want compile-time type safety
  • ✅ You want cleaner, more readable code

Use atomic.Value when:

  • ✅ You need to store different types dynamically
  • ✅ Working with non-pointer types
  • ✅ Using older Go versions
  • ✅ Interfacing with legacy code

For new code in Go 1.19+, atomic.Pointer[T] is generally the better choice for pointer operations due to its type safety, performance benefits, and cleaner API.

@fjl fjl changed the title metrics: using atomic.Pointer metrics: use atomic.Pointer in runtimeHistogram Aug 7, 2025
@fjl fjl merged commit 888b71b into ethereum:master Aug 7, 2025
4 of 5 checks passed
@fjl fjl added this to the 1.16.3 milestone Aug 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants