Files
bl/common/utils/lockfree-1.1.3/producer_test.go
昔念 269256a861 feat(common): 添加无锁并发工具包依赖
新增 lockfree-1.1.3 工具包到 go.work 文件中,为项目提供无锁并发数据结构支持,
提升高并发场景下的性能表现。
2025-12-05 00:36:28 +08:00

223 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
package lockfree
import (
"fmt"
"math/rand"
"os"
"reflect"
"sync"
"sync/atomic"
"testing"
"time"
"unsafe"
)
const (
GoSize = 5000
SchPerGo = 10000
)
type longEventHandler[T uint64] struct {
count int32
ts time.Time
}
func (h *longEventHandler[T]) OnEvent(v uint64) {
atomic.AddInt32(&h.count, 1)
if h.count == 1 {
h.ts = time.Now()
}
fmt.Println("consumer ", v)
//if v%1000000 == 0 {
// fmt.Printf("read %d\n", v)
//}
if h.count == GoSize*SchPerGo {
tl := time.Since(h.ts)
fmt.Printf("read time = %d ms\n", tl.Milliseconds())
}
}
func TestAA(t *testing.T) {
var (
t1_10us = uint64(0) // 1-10微秒
t10_100us = uint64(0) // 10-100微秒
t100_1000us = uint64(0) // 100-1000微秒
t1_10ms = uint64(0) // 1-10毫秒
t10_100ms = uint64(0) // 10-100毫秒
t100_ms = uint64(0) // 大于100毫秒
slower = uint64(0)
counter = uint64(0)
)
eh := &longEventHandler[uint64]{}
//queue, err := NewProducer[uint64](1024*1024, 1, eh, &SleepWaitStrategy{
// t: time.Nanosecond * 1,
//})
disruptor := NewLockfree[uint64](1024*1024*128, eh,
NewSleepBlockStrategy(time.Microsecond))
disruptor.Start()
producer := disruptor.Producer()
var wg sync.WaitGroup
wg.Add(GoSize)
totalS := time.Now()
for i := 0; i < GoSize; i++ {
go func() {
for j := 0; j < SchPerGo; j++ {
x := atomic.AddUint64(&counter, 1)
ts := time.Now()
err := producer.Write(x)
if err != nil {
panic(err)
}
tl := time.Since(ts)
ms := tl.Microseconds()
if ms > 1 {
atomic.AddUint64(&slower, 1)
if ms < 10 { // t1_10us
atomic.AddUint64(&t1_10us, 1)
} else if ms < 100 {
atomic.AddUint64(&t10_100us, 1)
} else if ms < 1000 {
atomic.AddUint64(&t100_1000us, 1)
} else if ms < 10000 {
atomic.AddUint64(&t1_10ms, 1)
} else if ms < 100000 {
atomic.AddUint64(&t10_100ms, 1)
} else {
atomic.AddUint64(&t100_ms, 1)
}
}
}
wg.Done()
}()
}
wg.Wait()
totalL := time.Since(totalS)
fmt.Printf("write total time = [%d ms]\n", totalL.Milliseconds())
fmt.Println("----- write complete -----")
time.Sleep(time.Second * 3)
disruptor.Close()
fmt.Printf("slow ratio = %.2f \n", float64(slower)*100.0/float64(counter))
fmt.Printf("quick ratio = %.2f \n", float64(counter-slower)*100.0/float64(counter))
fmt.Printf("[<1us][%d] \n", counter-slower)
fmt.Printf("[1-10us][%d] \n", t1_10us)
fmt.Printf("[10-100us][%d] \n", t10_100us)
fmt.Printf("[100-1000us][%d] \n", t100_1000us)
fmt.Printf("[1-10ms][%d] \n", t1_10ms)
fmt.Printf("[10-100ms][%d] \n", t10_100ms)
fmt.Printf("[>100ms][%d] \n", t100_ms)
}
func TestB(t *testing.T) {
var x = uint64(100)
typeOf := reflect.TypeOf(x)
fmt.Println(typeOf)
fmt.Println(os.Getpagesize())
}
func TestC(t *testing.T) {
x := 128 - unsafe.Sizeof(uint64(0))%128
fmt.Println(x)
}
func TestProducer_WriteWindow(t *testing.T) {
eh := &sleepEventHandler[uint64]{
sm: time.Second,
}
disruptor := NewLockfree[uint64](1, eh,
NewSleepBlockStrategy(time.Microsecond))
disruptor.Start()
producer := disruptor.Producer()
// 写入10个数0-9
// 预期结果0可以写入写入后在1ms内被取走此时0会在等待1s后被打印但由于ringbuffer有空位所以1可以被写入
// 1写入后一直无法被取走因为在等待1s内0的打印后续其他值均无法被写入因为1导致ringbuffer满了
for i := 0; i < 10; i++ {
ww := producer.WriteWindow()
if ww <= 0 {
// 表示不能写入,丢弃
fmt.Println("discard ", i , " window ", ww)
} else {
// 实际写入
producer.Write(uint64(i))
}
// 为了给予consumer时间取走
time.Sleep(time.Millisecond)
}
// 为了可以看到打印的结果
time.Sleep(3 * time.Second)
disruptor.Close()
}
func TestWriteTimeout(t *testing.T) {
var counter = uint64(0)
// 写入超时,如何使用
eh := &longSleepEventHandler[uint64]{}
disruptor := NewLockfree[uint64](2, eh, NewSleepBlockStrategy(time.Microsecond))
disruptor.Start()
producer := disruptor.Producer()
// 假设有10个写g
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
for j := 0; j < 100; j++ {
v := atomic.AddUint64(&counter, 1)
wc, exist, err := producer.WriteTimeout(v, time.Millisecond)
if err != nil {
return
}
if !exist {
// 重复写入1次写入不成功则丢弃重新写其他的
if ok, _ := producer.WriteByCursor(v, wc); ok {
continue
}
fmt.Println("discard ", v)
// 重新生成值,一直等待写入
v = atomic.AddUint64(&counter, 1)
for {
if ok, _ := producer.WriteByCursor(v, wc); ok {
fmt.Println("write ", v, " with x times")
break
}
// 写入不成功则休眠防止CPU暴增
time.Sleep(100 * time.Microsecond)
}
} else {
fmt.Println("write ", v, " with 1 time")
}
}
wg.Done()
}()
}
wg.Wait()
time.Sleep(3 * time.Second)
disruptor.Close()
}
// sleepEventHandler 休眠性质的事件处理器
type sleepEventHandler[T uint64] struct {
sm time.Duration
}
func (h *sleepEventHandler[T]) OnEvent(v uint64) {
time.Sleep(h.sm)
fmt.Println("consumer ", v)
}
type longSleepEventHandler[T uint64] struct {
count int32
}
func (h *longSleepEventHandler[T]) OnEvent(v uint64) {
// 每次处理都会进行随机休眠,可以导致消费端变慢
intn := rand.Intn(1000)
time.Sleep(time.Duration(intn * 1000))
fmt.Println("consumer count ", atomic.AddInt32(&h.count, 1))
}