Hi. This is still not correct. Use the “for.. in b.N” as discussed in the blog 
in order to understand the per op difference - which will be more accurate for 
a microbenchmark timing,

But, if you really want to make a case that bit mask is slower than mod, then a 
simpler test would be better - you can do these ops on in loop using b.N to 
time the difference.



> On May 12, 2024, at 8:28 PM, Yuta MIYAKE <yuta_miy...@mercari.com> wrote:
> 
> 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 
> <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 
>> <applewebdata://6580EDE1-4ABA-43A4-BCDE-DB83F7616ADE>> 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- <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 
>> <applewebdata://6580EDE1-4ABA-43A4-BCDE-DB83F7616ADE>.
>> 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 
> <mailto: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
>  
> <https://groups.google.com/d/msgid/golang-nuts/efe2618f-c520-4b53-b233-a724fe9b77d5n%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/7008322F-1FE4-4922-AFEB-035037900616%40ix.netcom.com.

Reply via email to