Thank you. 

This is benchstat result. new test code follows:

❯ go test -test.bench BenchmarkTestSingleModulo -count=10 -cpu=1 > 
modulo.txt
❯ go test -test.bench BenchmarkTestSingleBitMask -count=10 -cpu=1 > 
bitmask.txt
❯ benchstat modulo.txt bitmask.txt
goos: darwin
goarch: arm64
pkg: ringbuffer
                  │ modulo.txt │    bitmask.txt    │
                  │   sec/op   │   sec/op    vs base   │
TestSingleModulo    6.648 ± 1%
TestSingleBitMask                6.694 ± 5%
geomean             6.648        6.694       ? ¹ ²


new test code:

const BufferSize = 2 * 1024 * 1024

func benchmarkSingle(rb RingBuffer) {
total := 500000
for i := 0; i < total; i++ {
for j := 0; j < 1000; j++ {
rb.Enqueue(j)
}
for j := 0; j < 1000; j++ {
rb.Dequeue()
}
}
}

func BenchmarkTestSingleModulo(b *testing.B) {
rb := NewRingBuffer0(BufferSize)
b.ResetTimer()
benchmarkSingle(rb)
}

func BenchmarkTestSingleBitMask(b *testing.B) {
rb := NewRingBuffer1(BufferSize)
b.ResetTimer()
benchmarkSingle(rb)
}

On Monday, May 13, 2024 at 8:20:05 AM UTC+9 robert engels wrote:

> Use the Go benchmarking facilities, see 
> https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go
>
> On May 11, 2024, at 9:57 PM, leon <leonca...@gmail.com> wrote:
>
> I'm trying to prove an optimization technique for ring buffer is 
> effective. One of the technique is using bitmask instead of modulo to 
> calculate a wrap around. However, in my environment, modulo is slightly 
> faster in a test where 1 billion items are enqueued /dequeued by a single 
> goroutine. What do you think could be the cause? 
>
> Full code:
> https://go.dev/play/p/H933oqrhPI-
>
> Environment:
> * go version go1.21.4 darwin/arm64
> * Apple M1 Pro
>
> RingBuffer with modulo:
> ```
> type RingBuffer0 struct {
> writeIdx uint64
> readIdx  uint64
> buffers  []any
> size     uint64
> }
>
> func NewRingBuffer0(size uint64) *RingBuffer0 {
> rb := &RingBuffer0{}
> rb.init(size)
> return rb
> }
>
> func (rb *RingBuffer0) init(size uint64) {
> rb.buffers = make([]any, size)
> rb.size = size
> }
>
> func (rb *RingBuffer0) Enqueue(item any) error {
> if rb.writeIdx-rb.readIdx == rb.size {
> return ErrBufferFull
> }
> rb.buffers[rb.writeIdx%rb.size] = item
> rb.writeIdx++
> return nil
> }
>
> func (rb *RingBuffer0) Dequeue() (any, error) {
> if rb.writeIdx == rb.readIdx {
> return nil, ErrBufferEmpty
> }
> item := rb.buffers[rb.readIdx%rb.size]
> rb.readIdx++
> return item, nil
> }
> ```
>
> RingBuffer with bitmask:
> change each module calculation to the code below
> * rb.buffers[rb.writeIdx&(rb.size-1)] = item
> * item := rb.buffers[rb.readIdx&(rb.size-1)]
>
> Test:
> func TestSingle(rb RingBuffer) {
> start := time.Now()
> total := 500000
> for i := 0; i < total; i++ {
> for j := 0; j < 1000; j++ {
> rb.Enqueue(j)
> }
> for j := 0; j < 1000; j++ {
> rb.Dequeue()
> }
> }
> end := time.Now()
> count := total * 2000
> duration := end.Sub(start).Milliseconds()
> fmt.Printf("%d ops in %d ms\n", count, duration)
> }
>
>
> -- 
> You received this message because you are subscribed to the Google Groups 
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/golang-nuts/b9c4d2e0-4ab4-4d27-9359-abd8c090ae33n%40googlegroups.com
>  
> <https://groups.google.com/d/msgid/golang-nuts/b9c4d2e0-4ab4-4d27-9359-abd8c090ae33n%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/efe2618f-c520-4b53-b233-a724fe9b77d5n%40googlegroups.com.

Reply via email to