Skip to content
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

hash/maphash: add Bytes and String #42710

Closed
dsnet opened this issue Nov 19, 2020 · 28 comments
Closed

hash/maphash: add Bytes and String #42710

dsnet opened this issue Nov 19, 2020 · 28 comments

Comments

@dsnet
Copy link
Member

dsnet commented Nov 19, 2020

Update, Mar 16 2022 - Current proposal is #42710 (comment).


The overhead of constructing a Hash object in order to call Hash.Write greatly diminishes its utility for small buffers.

Consider the following micro-benchmark:

var source = []byte("hello, world") // 12 bytes long
var sink uint64

func BenchmarkMapHash(b *testing.B) {
	var seed = maphash.MakeSeed()
	for i := 0; i < b.N; i++ {
		var h maphash.Hash
		h.SetSeed(seed)
		h.Write(source)
		sink = h.Sum64()
	}
}

//go:linkname runtime_memhash runtime.memhash
//go:noescape
func runtime_memhash(p unsafe.Pointer, seed, s uintptr) uintptr

func BenchmarkUnsafeHash(b *testing.B) {
	for i := 0; i < b.N; i++ {
		sink = uint64(runtime_memhash(*(*unsafe.Pointer)(unsafe.Pointer(&source)), 0, uintptr(len(source))))
	}
}

On my machine this produces:

BenchmarkMapHash      	56459700	        21.6 ns/op
BenchmarkUnsafeHash   	246443527	        4.90 ns/op

Directly using runtime_memhash is around ~4x faster since it avoids all the unnecessary state contained by Hash that are relatively pointless when hashing a single small string.

I propose adding the following API:

func Sum(b []byte, seed Seed) uint64

where the function is a thin wrapper over runtime_memhash. It takes Seed as an input to force the user to think about the application of seeds.

I chose the name Sum to be consistent with md5.Sum or sha1.Sum.

@gopherbot gopherbot added this to the Proposal milestone Nov 19, 2020
@ianlancetaylor ianlancetaylor added this to Incoming in Proposals (old) Nov 19, 2020
@mpx
Copy link
Contributor

mpx commented Nov 19, 2020

I found hash/maphash was unusable for string hashing in a custom map implementation due to performance. It was better to use runtime.strhash directly (..and accept potential future maintenance hassle).

Ideally maphash.Sum([]byte(s), seed) would also be Fast, but this requires some compiler improvements. Maybe SumString should be added as well to address this use case?

@dsnet
Copy link
Member Author

dsnet commented Nov 19, 2020

Ideally maphash.Sum([]byte(s), seed) would also be Fast, but this requires some compiler improvements. Maybe SumString should be added as well to address this use case?

See #2205. I didn't propose SumString cause I'm still hoping for a compiler optimization to avoid these cases.

@rsc
Copy link
Contributor

rsc commented Dec 2, 2020

for i := 0; i < b.N; i++ {
	var h maphash.Hash
	h.SetSeed(seed)
	h.Write(source)
	sink = h.Sum64()
}

This is not how it's supposed to be used. You're supposed to do:

var h maphash.Hash
h.SetSeed(seed)
for i := 0; i < b.N; i++ {
	h.Reset()
	h.Write(source)
	sink = h.Sum64()
}

Can you try that instead?

@rsc rsc moved this from Incoming to Active in Proposals (old) Dec 2, 2020
@dsnet
Copy link
Member Author

dsnet commented Dec 2, 2020

Can you try that instead?

The runtime drops from ~22ns to 20ns, but it's still much slower compared to a direct hash on a []byte (~5ns).

@rsc
Copy link
Contributor

rsc commented Dec 9, 2020

@dsnet, is there a fundamental reason why Write+Sum64 should be so much slower than a single call?
Back when SetSeed was in the loop, I could see SetSeed being fundamentally unnecessarily expensive.
But why is Write+Sum64 fundamentally slower than one call? Do we just have a performance bug somewhere?

@dsnet
Copy link
Member Author

dsnet commented Dec 9, 2020

I see several reasons for slowdown:

  • Hash.Write always performs a copy on the input slice. It's not clear to me how to avoid the copy without fundamentally changing the operation of how Hash works. For hashing short strings, half the time is spent on copying (i.e., avoiding the copy brings runtime to 10ns).
  • Hash.Reset, Hash.Write, Hash.Sum64, and Hash.initSeed are not inlineable. With some massaging, we can probably get all of them except Hash.Write to be inlined. Doing so saves a few more ns.

@rsc
Copy link
Contributor

rsc commented Dec 16, 2020

I see. So the copy during the large writes is a bug - we should just hash those bytes directly. Then the benchmark, which hashes a multiple of 64 bytes, would have no copies at all. At that point it seems like the only win is Write+Sum64 combined knowing that the final copy isn't needed. But maybe that's too small a win.

@gopherbot
Copy link

Change https://golang.org/cl/278758 mentions this issue: hash/maphash: optimize Write and WriteString

@gopherbot
Copy link

Change https://golang.org/cl/278759 mentions this issue: hash/maphash: manually inline setSeed

@josharian
Copy link
Contributor

josharian commented Dec 17, 2020

I played with this a bit, because I also hit this recently. CLs 278758 and 278759 and 278760 were what I could squeeze out of it. They help a fair amount for large writes, but not much for little ones, which is what I'm working on (trying to reduce the performance regression due to tailscale/tailscale@aa9d7f4).

@gopherbot
Copy link

Change https://golang.org/cl/278760 mentions this issue: hash/maphash: increase the buffer size

@rsc
Copy link
Contributor

rsc commented Jan 6, 2021

Thanks for working on optimizations, @josharian. We should probably put this proposal on hold until we've made the current API as fast as it can be.

@josharian
Copy link
Contributor

FWIW, I've optimized as much as I know how. I don't see a way to make the current API any faster for small writes. The need to make writes work identically regardless of how they got chunked really ties the hands of the implementation.

@rsc
Copy link
Contributor

rsc commented Jan 6, 2021

Placed on hold.
— rsc for the proposal review group

@rsc rsc moved this from Active to Hold in Proposals (old) Jan 6, 2021
@alandonovan
Copy link
Contributor

While attempting to fix google/starlark-go#16 I rediscovered this issue and was about to file a bug report, but then stumbled on this one. I'll share my benchmark here in case it's useful.

package test_test                                                                                                                                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                                                                                                                
import (                                                                                                                                                                                                                                                                                                                                                                                        
        "fmt"                                                                                                                                                                                                                                                                                                                                                                                   
        "hash/maphash"                                                                                                                                                                                                                                                                                                                                                                          
        "math/rand"                                                                                                                                                                                                                                                                                                                                                                             
        "testing"                                                                                                                                                                                                                                                                                                                                                                               
        _ "unsafe" // for linkname hack                                                                                                                                                                                                                                                                                                                                                         
)                                                                                                                                                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                                                                                                                
// fnv is the fastest for size < 16, runtime is asymptotically fastest.                                                                                                                                                                                                                                                                                                                         
// maphash only beats fnv at when size >= 128, and is 6x slower than runtime                                                                                                                                                                                                                                                                                                                    
// when size = 1024 even though it's the same algorithm.                                                                                                                                                                                                                                                                                                                                        
//                                                                                                                                                                                                                                                                                                                                                                                              
// Benchmark/maphash-1-12               65962508                17.13 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/fnv-1-12                   1000000000               0.6944 ns/op                                                                                                                                                                                                                                                                                                                   
// Benchmark/runtime-1-12               291040464                3.793 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/maphash-2-12               69972840                17.02 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/fnv-2-12                   1000000000               1.052 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/runtime-2-12               282301180                3.786 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/maphash-4-12               74032375                16.76 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/fnv-4-12                   594186979                1.727 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/runtime-4-12               278877213                4.043 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/maphash-8-12               70253731                16.79 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/fnv-8-12                   420115022                2.600 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/runtime-8-12               291869521                3.903 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/maphash-16-12              72486172                16.24 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/fnv-16-12                  249103671                4.397 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/runtime-16-12              300819609                3.706 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/maphash-32-12              83406832                13.99 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/fnv-32-12                  140682667                8.300 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/runtime-32-12              292491626                4.171 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/maphash-64-12              72692757                16.07 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/fnv-64-12                  74951726                15.94 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/runtime-64-12              200250006                5.884 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/maphash-128-12             43835584                26.30 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/fnv-128-12                 33626848                35.25 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/runtime-128-12             131992580                8.546 ns/op                                                                                                                                                                                                                                                                                                                    
// Benchmark/maphash-256-12             24941272                48.08 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/fnv-256-12                 16829190                66.17 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/runtime-256-12             102915433               11.18 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/maphash-512-12             10526881                96.05 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/fnv-512-12                  9160648               127.0 ns/op                                                                                                                                                                                                                                                                                                                      
// Benchmark/runtime-512-12             64771675                18.82 ns/op                                                                                                                                                                                                                                                                                                                     
// Benchmark/maphash-1024-12             6152947               193.3 ns/op                                                                                                                                                                                                                                                                                                                      
// Benchmark/fnv-1024-12                 4535538               248.3 ns/op                                                                                                                                                                                                                                                                                                                      
// Benchmark/runtime-1024-12            35820849                33.13 ns/op                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                                
func Benchmark(b *testing.B) {                                                                                                                                                                                                                                                                                                                                                                  
        for size := 1; size <= 1024; size *= 2 {                                                                                                                                                                                                                                                                                                                                                
                buf := make([]byte, size)                                                                                                                                                                                                                                                                                                                                                       
                rand.New(rand.NewSource(0)).Read(buf)                                                                                                                                                                                                                                                                                                                                           
                s := string(buf)                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                
                b.Run(fmt.Sprintf("maphash-%d", size), func(b *testing.B) {                                                                                                                                                                                                                                                                                                                     
                        for i := 0; i < b.N; i++ {                                                                                                                                                                                                                                                                                                                                              
                                maphashString(s)                                                                                                                                                                                                                                                                                                                                                
                        }                                                                                                                                                                                                                                                                                                                                                                       
                })                                                                                                                                                                                                                                                                                                                                                                              
                b.Run(fmt.Sprintf("fnv-%d", size), func(b *testing.B) {                                                                                                                                                                                                                                                                                                                         
                        for i := 0; i < b.N; i++ {                                                                                                                                                                                                                                                                                                                                              
                                fnvHashString(s)                                                                                                                                                                                                                                                                                                                                                
                        }                                                                                                                                                                                                                                                                                                                                                                       
                })                                                                                                                                                                                                                                                                                                                                                                              
                b.Run(fmt.Sprintf("runtime-%d", size), func(b *testing.B) {                                                                                                                                                                                                                                                                                                                     
                        for i := 0; i < b.N; i++ {                                                                                                                                                                                                                                                                                                                                              
                                runtimeHashString(s, 0)                                                                                                                                                                                                                                                                                                                                         
                        }                                                                                                                                                                                                                                                                                                                                                                       
                })                                                                                                                                                                                                                                                                                                                                                                              
        }                                                                                                                                                                                                                                                                                                                                                                                       
}                                                                                                                                                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                                                                                                                
// maphashString computes the hash of a string using the maphash package,                                                                                                                                                                                                                                                                                                                       
// which is a wrapper around the implementation used by runtimeHashString.                                                                                                                                                                                                                                                                                                                      
func maphashString(s string) uint32 {                                                                                                                                                                                                                                                                                                                                                           
        var h maphash.Hash                                                                                                                                                                                                                                                                                                                                                                      
        h.SetSeed(seed)                                                                                                                                                                                                                                                                                                                                                                         
        h.WriteString(s)                                                                                                                                                                                                                                                                                                                                                                        
        sum := h.Sum64()                                                                                                                                                                                                                                                                                                                                                                        
        return uint32(sum>>32) ^ uint32(sum&0xFFFFFFFF)                                                                                                                                                                                                                                                                                                                                         
}                                                                                                                                                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                                                                                                                
var seed = maphash.MakeSeed()                                                                                                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                                                                                                                
// runtimeHashString computes the hash of a string using amd64 AESINC hardware.                                                                                                                                                                                                                                                                                                                 
//go:linkname runtimeHashString runtime.stringHash                                                                                                                                                                                                                                                                                                                                              
func runtimeHashString(s string, seed uintptr) uintptr                                                                                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                                                                                                                
// fnvHashString computes the FNV hash of s in software.                                                                                                                                                                                                                                                                                                                                        
func fnvHashString(s string) uint32 {                                                                                                                                                                                                                                                                                                                                                           
        var h uint32                                                                                                                                                                                                                                                                                                                                                                            
        for i := 0; i < len(s); i++ {                                                                                                                                                                                                                                                                                                                                                           
                h ^= uint32(s[i])                                                                                                                                                                                                                                                                                                                                                               
                h *= 16777619                                                                                                                                                                                                                                                                                                                                                                   
        }                                                                                                                                                                                                                                                                                                                                                                                       
        return h                                                                                                                                                                                                                                                                                                                                                                                
}                                                                                                                                                                                                                                                                                                                                                                                               

gopherbot pushed a commit that referenced this issue Mar 11, 2021
Provides a minor performance win.

name            old time/op    new time/op    delta
Hash8Bytes-8      16.5ns ± 2%    16.5ns ± 4%    ~     (p=0.407 n=27+29)
Hash320Bytes-8    58.5ns ± 2%    55.0ns ± 2%  -6.01%  (p=0.000 n=29+28)
Hash1K-8           195ns ± 1%     190ns ± 2%  -2.23%  (p=0.000 n=30+30)
Hash8K-8          1.59µs ± 2%    1.57µs ± 2%  -0.88%  (p=0.002 n=30+30)

name            old speed      new speed      delta
Hash8Bytes-8     484MB/s ± 2%   485MB/s ± 4%    ~     (p=0.417 n=27+29)
Hash320Bytes-8  5.47GB/s ± 2%  5.82GB/s ± 2%  +6.39%  (p=0.000 n=29+28)
Hash1K-8        5.26GB/s ± 1%  5.39GB/s ± 2%  +2.29%  (p=0.000 n=30+30)
Hash8K-8        5.16GB/s ± 2%  5.21GB/s ± 2%  +0.89%  (p=0.002 n=30+30)

Updates #42710

Change-Id: Ia0d7264b648f96099202de21c6b69a9c1776f6c8
Reviewed-on: https://go-review.googlesource.com/c/go/+/278759
Trust: Josh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
gopherbot pushed a commit that referenced this issue Mar 11, 2021
The existing code makes copies of every byte it hashes.
When passed a large chunk of memory, Write and WriteString
can skip the copying and initSeed for most of it.

To ensure that Write, WriteByte, and WriteString continue to
generate output that depends only on the sequence of bytes,
expand the grouping test to include WriteString and interleaved calls.
Also, make the test process a lot more data, to ensure that
Write* handled full buffers correctly.

name            old time/op    new time/op    delta
Hash8Bytes-8      17.1ns ± 3%    16.5ns ± 2%   -3.26%  (p=0.000 n=29+27)
Hash320Bytes-8    74.9ns ± 2%    58.5ns ± 2%  -21.86%  (p=0.000 n=30+29)
Hash1K-8           246ns ± 3%     195ns ± 1%  -20.82%  (p=0.000 n=29+30)
Hash8K-8          1.87µs ± 2%    1.59µs ± 2%  -15.04%  (p=0.000 n=26+30)

name            old speed      new speed      delta
Hash8Bytes-8     468MB/s ± 3%   484MB/s ± 2%   +3.36%  (p=0.000 n=29+27)
Hash320Bytes-8  4.28GB/s ± 2%  5.47GB/s ± 2%  +27.97%  (p=0.000 n=30+29)
Hash1K-8        4.17GB/s ± 3%  5.26GB/s ± 1%  +26.28%  (p=0.000 n=29+30)
Hash8K-8        4.38GB/s ± 2%  5.16GB/s ± 2%  +17.70%  (p=0.000 n=26+30)

Updates #42710

Change-Id: If3cdec1580ffb3e36fab9865e5a9d089c0a34bec
Reviewed-on: https://go-review.googlesource.com/c/go/+/278758
Trust: Josh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
gopherbot pushed a commit that referenced this issue Mar 11, 2021
This helps a lot for larger writes.

name            old time/op    new time/op    delta
Hash8Bytes-8      16.5ns ± 4%    16.8ns ± 2%   +1.76%  (p=0.000 n=29+30)
Hash320Bytes-8    55.0ns ± 2%    41.4ns ± 2%  -24.64%  (p=0.000 n=28+28)
Hash1K-8           190ns ± 2%     130ns ± 3%  -31.65%  (p=0.000 n=30+30)
Hash8K-8          1.57µs ± 2%    1.05µs ± 3%  -33.01%  (p=0.000 n=30+29)

name            old speed      new speed      delta
Hash8Bytes-8     485MB/s ± 4%   476MB/s ± 2%   -1.73%  (p=0.000 n=29+30)
Hash320Bytes-8  5.82GB/s ± 2%  7.72GB/s ± 3%  +32.55%  (p=0.000 n=28+29)
Hash1K-8        5.39GB/s ± 2%  7.88GB/s ± 3%  +46.32%  (p=0.000 n=30+30)
Hash8K-8        5.21GB/s ± 2%  7.77GB/s ± 3%  +49.28%  (p=0.000 n=30+29)

Updates #42710

Change-Id: Idaf4b2a8a41fc62fc16b54c9358cf2cc7009cf29
Reviewed-on: https://go-review.googlesource.com/c/go/+/278760
Trust: Josh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
@josharian
Copy link
Contributor

All my optimizations are in. I still think we need new API to make small writes faster.

@kixelated
Copy link
Contributor

kixelated commented Jan 15, 2022

Ran into this issue when benchmarking my application.

Here's the worst case scenario:

func BenchmarkMaphash(b *testing.B) {
    var hash maphash.Hash
    var data [127]byte

    for i := 0; i < b.N; i += 1 {
        hash.Reset()
        hash.Write(data[:])
        hash.Sum64()
    }
}

27% of the time is spent in Write, where none of the hashing takes place. Like mentioned earlier, this is due to copying the 127 bytes into the buffer, so the hash can be computed when Sum64 is called. Only 53% of the time was actually spent in rthash; the rest was spent record keeping.

I would propose adding a Sum method to Seed:

seed := maphash.MakeSeed()
sum := seed.Sum64(data)

IMO this fits the API better than a function that takes Seed as an argument.

@kixelated
Copy link
Contributor

This proposal would also make the code more readable.

Before:

var seed = maphash.MakeSeed() // global

var hash maphash.Hash
hash.SetSeed(seed)
_, _ = hash.Write(data)
sum := hash.Sum64()

After:

var seed = maphash.MakeSeed() // global

sum := seed.Sum64(data)

@kixelated
Copy link
Contributor

kixelated commented Jan 19, 2022

For some more context, I'm hashing a set of strings and a unix timestamp (int64) to use as a map key.

The old code used to sort the strings, append them together, and use strconv to build a string as a key. The new code uses maphash to hash each string and XOR the hashes together (so order is irrelevant). Then I hash the timestamp with a different seed and XOR it to the hash. It's 5x faster now and has reduced my service's CPU usage by 5%.

I'm using the unsafe code in this ticket and it works great. However, I had to fork it into string and int64 variants. Casting the string to []byte was more expensive than I had anticipated. For the best performance, it would actually be necessary to expose the type-specific hash functions as defined in src/runtime/alg.go.

Maybe something like?

var nameSeed = maphash.MakeSeed()
var timeSeed = maphash.MakeSeed()

func myHash(names []string, timestamp int64) (sum uint64) {
  for _, name := range names {
    sum ^= nameSeed.HashString(name)
  }

  sum ^= timeSeed.HashInt64(timeSeed)
  return sum
}

The current maphash API is inadequate because of the aforementioned buffer and it would require the names to be sorted.

@bradfitz
Copy link
Contributor

People were pinging me privately to unhold this per comments above, so done.

@ianlancetaylor ianlancetaylor moved this from Hold to Active in Proposals (old) Feb 18, 2022
@gopherbot
Copy link

Change https://go.dev/cl/392494 mentions this issue: hash/maphash: add BytesSum64 and StringSum64

@rsc
Copy link
Contributor

rsc commented Mar 14, 2022

Thanks @josharian for optimizing the current API. It looks like you got about 2X, and that there's still about 2X on the table. I mailed CL 392494 showing the 2X that remains.

The remaining 2X gap seems fundamental, so it seems reasonable to add new API.

I hesitate to add methods directly on maphash.Seed. That seems like confusing different concepts. And adding new concurrency-safe methods on maphash.Hash confuses when a Hash can be shared by multiple goroutines. That leaves top-level functions, analogous to sha1.Sum and so on, as @dsnet originally suggested.

@rsc
Copy link
Contributor

rsc commented Mar 14, 2022

In the CL I used:

func BytesSum64(Seed, []byte) uint64
func StringSum64(Seed, string) uint64

Thumbs up/down: do people think it would be better to use

func Bytes(Seed, []byte) uint64
func String(Seed, string) uint64

?

@dsnet
Copy link
Member Author

dsnet commented Mar 14, 2022

What about?

func Sum[Bytes interface{ []byte | string }](b Bytes) uint64

@rsc
Copy link
Contributor

rsc commented Mar 14, 2022

It's unclear that generics are making things clearer in that case, and I don't know how to implement the function body.

@rsc rsc changed the title proposal: hash/maphash: add Sum proposal: hash/maphash: add Bytes and String Mar 16, 2022
@rsc
Copy link
Contributor

rsc commented Mar 16, 2022

Does anyone object to adding Bytes and String as described in #42710 (comment)?

@rsc
Copy link
Contributor

rsc commented Mar 23, 2022

Based on the discussion above, this proposal seems like a likely accept.
— rsc for the proposal review group

@rsc rsc moved this from Active to Likely Accept in Proposals (old) Mar 23, 2022
@rsc rsc moved this from Likely Accept to Accepted in Proposals (old) Mar 30, 2022
@rsc
Copy link
Contributor

rsc commented Mar 30, 2022

No change in consensus, so accepted. 🎉
This issue now tracks the work of implementing the proposal.
— rsc for the proposal review group

@rsc rsc changed the title proposal: hash/maphash: add Bytes and String hash/maphash: add Bytes and String Mar 30, 2022
@rsc rsc modified the milestones: Proposal, Backlog Mar 30, 2022
@golang golang locked and limited conversation to collaborators Apr 5, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
No open projects
Development

No branches or pull requests

8 participants