From 5483c916aa485c7882c5c9a2467c27453201ae5c Mon Sep 17 00:00:00 2001 From: xgzlucario <912156837@qq.com> Date: Sat, 3 Aug 2024 22:17:48 +0800 Subject: [PATCH] docs: upload bench image --- .gitignore | 2 ++ README.md | 30 +++++++++++++++++++++--------- README_CN.md | 30 +++++++++++++++++++++--------- bench.jpg | Bin 48798 -> 47783 bytes bench.sh | 30 ++++++++++++++++++++++++++++++ command.go | 15 +++++++-------- internal/list/list.go | 12 +++++++----- internal/list/listpack.go | 2 +- output/bench.csv | 9 +++++++++ 9 files changed, 98 insertions(+), 32 deletions(-) create mode 100755 bench.sh create mode 100644 output/bench.csv diff --git a/.gitignore b/.gitignore index b91399d..ec5af87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ coverage.* *.aof + +redis rotom \ No newline at end of file diff --git a/README.md b/README.md index 433922c..2d76816 100644 --- a/README.md +++ b/README.md @@ -45,25 +45,37 @@ Rotom has made several optimizations in data structures: Notably, `zipmap` and `zipset` are space-efficient data structures based on `listpack`, which is a new compressed list proposed by Redis to replace `ziplist`, supporting both forward and reverse traversal and solving the cascading update issue in `ziplist`. -## Roadmap - -- Support for LRU cache and memory eviction. -- Support for gradual rehashing in dict. -- Support for RDB and AOF Rewrite. -- Compatibility with more commonly used commands. - ## Benchmark +![img](bench.jpg) + The test will run rotom on the same machine with `appendonly` disabled, and use `redis-benchmark` tool to test the latency of different commands. ``` goos: linux goarch: amd64 pkg: github.com/xgzlucario/rotom -cpu: Intel(R) Core(TM) i7-10700 CPU @ 2.90GHz +cpu: 13th Gen Intel(R) Core(TM) i5-13600KF ``` -![img](bench.jpg) +``` + redis rotom redis_P10 rotom_P10 redis_P50 rotom_P50 +SET 268817 268817 2222222 2173913 3448276 5263158 +GET 265957 259740 2702702 1818181 4347826 4545454 +INCR 271739 261780 2500000 2439024 4347826 7692307 +LPUSH 289017 282485 2083333 2272727 2941176 4347826 +RPUSH 283286 271739 2272727 2439024 3333333 7692307 +SADD 273972 269541 2439024 2631579 4000000 7142857 +HSET 282485 277777 2000000 2127659 3030303 3703703 +ZADD 273224 272479 1960784 2702702 2941176 6249999 +``` + +## Roadmap + +- Support for LRU cache and memory eviction. +- Support for gradual rehashing in dict. +- Support for RDB and AOF Rewrite. +- Compatibility with more commonly used commands. ## Usage diff --git a/README_CN.md b/README_CN.md index 079aff1..d56cc4c 100644 --- a/README_CN.md +++ b/README_CN.md @@ -45,25 +45,37 @@ rotom 在数据结构上做了许多优化: 值得一提的是,`zipmap` 和 `zipset` 是空间紧凑的数据结构,它们都基于 `listpack`, 这是 Redis 提出的替代 `ziplist` 的新型压缩列表,支持正序及逆序遍历,解决了 `ziplist` 存在级联更新的问题。 -## 计划 - -- LRU 缓存及内存淘汰支持 -- dict 渐进式哈希支持 -- RDB 及 AOF Rewrite 支持 -- 兼容更多常用命令 - ## 性能 +![img](bench.jpg) + 测试将在同一台机器上,关闭 `appendonly`,并使用 `redis-benchmark` 工具测试不同命令的 qps。 ``` goos: linux goarch: amd64 pkg: github.com/xgzlucario/rotom -cpu: Intel(R) Core(TM) i7-10700 CPU @ 2.90GHz +cpu: 13th Gen Intel(R) Core(TM) i5-13600KF ``` -![img](bench.jpg) +``` + redis rotom redis_P10 rotom_P10 redis_P50 rotom_P50 +SET 268817 268817 2222222 2173913 3448276 5263158 +GET 265957 259740 2702702 1818181 4347826 4545454 +INCR 271739 261780 2500000 2439024 4347826 7692307 +LPUSH 289017 282485 2083333 2272727 2941176 4347826 +RPUSH 283286 271739 2272727 2439024 3333333 7692307 +SADD 273972 269541 2439024 2631579 4000000 7142857 +HSET 282485 277777 2000000 2127659 3030303 3703703 +ZADD 273224 272479 1960784 2702702 2941176 6249999 +``` + +## 计划 + +- LRU 缓存及内存淘汰支持 +- dict 渐进式哈希支持 +- RDB 及 AOF Rewrite 支持 +- 兼容更多常用命令 ## 使用 diff --git a/bench.jpg b/bench.jpg index c93baf725a57ed36fe70211ff1f42f1302c5a92f..ee4c5a4b0cadd19d733bc3fccc5c19f917d23d40 100644 GIT binary patch literal 47783 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 l@qgl-|9by-ICgOrle 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 G?)(or`N^yR diff --git a/bench.sh b/bench.sh new file mode 100755 index 0000000..aeb9369 --- /dev/null +++ b/bench.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +TEST_NAME=$1 + +IMAGE_NAME=$TEST_NAME + +OUTPUT_FILE="output/$TEST_NAME" + +COMMANDS="set,get,incr,lpush,rpush,hset,sadd,zadd" + +PIPELINES=(1 10 50) + +mkdir -p output + +# clear output file +> $OUTPUT_FILE + +docker run --rm -d --name bench-test $IMAGE_NAME +sleep 3 + +# run bench +for pipeline in "${PIPELINES[@]}"; do + echo "Testing with pipeline: $pipeline" | tee -a $OUTPUT_FILE + docker exec bench-test redis-benchmark --csv -t $COMMANDS -P $pipeline | tee -a $OUTPUT_FILE + echo "" | tee -a $OUTPUT_FILE +done + +docker stop bench-test + +echo "Benchmarking completed. Results are saved in $OUTPUT_FILE." diff --git a/command.go b/command.go index be12f77..331e053 100644 --- a/command.go +++ b/command.go @@ -165,7 +165,7 @@ func delCommand(writer *RESPWriter, args []RESP) { } func hsetCommand(writer *RESPWriter, args []RESP) { - hash := args[0].ToString() + hash := args[0].ToStringUnsafe() args = args[1:] if len(args)%2 == 1 { @@ -228,13 +228,11 @@ func hdelCommand(writer *RESPWriter, args []RESP) { func hgetallCommand(writer *RESPWriter, args []RESP) { hash := args[0].ToStringUnsafe() - hmap, err := fetchMap(hash) if err != nil { writer.WriteError(err) return } - writer.WriteArrayHead(hmap.Len() * 2) hmap.Scan(func(key string, value []byte) { writer.WriteBulkString(key) @@ -243,7 +241,7 @@ func hgetallCommand(writer *RESPWriter, args []RESP) { } func lpushCommand(writer *RESPWriter, args []RESP) { - key := args[0].ToString() + key := args[0].ToStringUnsafe() ls, err := fetchList(key, true) if err != nil { writer.WriteError(err) @@ -256,7 +254,7 @@ func lpushCommand(writer *RESPWriter, args []RESP) { } func rpushCommand(writer *RESPWriter, args []RESP) { - key := args[0].ToString() + key := args[0].ToStringUnsafe() ls, err := fetchList(key, true) if err != nil { writer.WriteError(err) @@ -328,7 +326,7 @@ func lrangeCommand(writer *RESPWriter, args []RESP) { } func saddCommand(writer *RESPWriter, args []RESP) { - key := args[0].ToString() + key := args[0].ToStringUnsafe() args = args[1:] set, err := fetchSet(key, true) @@ -382,7 +380,7 @@ func spopCommand(writer *RESPWriter, args []RESP) { } func zaddCommand(writer *RESPWriter, args []RESP) { - key := args[0].ToString() + key := args[0].ToStringUnsafe() args = args[1:] zset, err := fetchZSet(key, true) @@ -564,7 +562,8 @@ func fetch[T any](key string, new func() T, setnx ...bool) (T, error) { v := new() if len(setnx) > 0 && setnx[0] { - db.dict.Set(key, v) + // make sure `key` is copy + db.dict.Set(strings.Clone(key), v) } return v, nil diff --git a/internal/list/list.go b/internal/list/list.go index 6c6b317..51ec812 100644 --- a/internal/list/list.go +++ b/internal/list/list.go @@ -10,6 +10,7 @@ import "math" // QuickList is double linked listpack, implement redis quicklist data structure, // based on listpack rather than ziplist to optimize cascade update. type QuickList struct { + size int head, tail *Node } @@ -36,6 +37,7 @@ func (ls *QuickList) LPush(key string) { ls.head.prev = n ls.head = n } + ls.size++ ls.head.LPush(key) } @@ -47,6 +49,7 @@ func (ls *QuickList) RPush(key string) { n.prev = ls.tail ls.tail = n } + ls.size++ ls.tail.RPush(key) } @@ -54,6 +57,7 @@ func (ls *QuickList) RPush(key string) { func (ls *QuickList) LPop() (key string, ok bool) { for lp := ls.head; lp != nil; lp = lp.next { if lp.size > 0 { + ls.size-- return lp.LPop() } ls.free(lp) @@ -65,6 +69,7 @@ func (ls *QuickList) LPop() (key string, ok bool) { func (ls *QuickList) RPop() (key string, ok bool) { for lp := ls.tail; lp != nil; lp = lp.prev { if lp.size > 0 { + ls.size-- return lp.RPop() } ls.free(lp) @@ -82,11 +87,8 @@ func (ls *QuickList) free(n *Node) { } } -func (ls *QuickList) Size() (n int) { - for lp := ls.head; lp != nil; lp = lp.next { - n += lp.Size() - } - return +func (ls *QuickList) Size() int { + return ls.size } func (ls *QuickList) Range(start, end int, f func(data []byte)) { diff --git a/internal/list/listpack.go b/internal/list/listpack.go index c7ea8f6..7d90e00 100644 --- a/internal/list/listpack.go +++ b/internal/list/listpack.go @@ -8,7 +8,7 @@ import ( ) const ( - maxListPackSize = 8 * 1024 + maxListPackSize = 16 * 1024 ) var ( diff --git a/output/bench.csv b/output/bench.csv new file mode 100644 index 0000000..204bd76 --- /dev/null +++ b/output/bench.csv @@ -0,0 +1,9 @@ + redis rotom redis_P10 rotom_P10 redis_P50 rotom_P50 +SET 268817 268817 2222222 2173913 3448276 5263158 +GET 265957 259740 2702702 1818181 4347826 4545454 +INCR 271739 261780 2500000 2439024 4347826 7692307 +LPUSH 289017 282485 2083333 2272727 2941176 4347826 +RPUSH 283286 271739 2272727 2439024 3333333 7692307 +SADD 273972 269541 2439024 2631579 4000000 7142857 +HSET 282485 277777 2000000 2127659 3030303 3703703 +ZADD 273224 272479 1960784 2702702 2941176 6249999 \ No newline at end of file