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 <[email protected]> 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 [email protected].
> 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 [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/golang-nuts/efe2618f-c520-4b53-b233-a724fe9b77d5n%40googlegroups.com.