Closed codingskynet closed 2 years ago
Benchmark: cargo criterion --bench concurrnet (only run bench_mixed_seqlockavltree) Envirnment:
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 1 threads
time: [627.13 ms 637.08 ms 641.85 ms]
thrpt: [779.00 Kelem/s 784.83 Kelem/s 797.28 Kelem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 2 threads
time: [743.90 ms 752.42 ms 758.41 ms]
thrpt: [1.3185 Melem/s 1.3290 Melem/s 1.3443 Melem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 4 threads
time: [918.26 ms 922.39 ms 929.03 ms]
thrpt: [2.1528 Melem/s 2.1683 Melem/s 2.1780 Melem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 8 threads
time: [1.4921 s 1.4989 s 1.5054 s]
thrpt: [2.6571 Melem/s 2.6687 Melem/s 2.6808 Melem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 16 threads
time: [2.7280 s 3.7645 s 5.1387 s]
thrpt: [1.5568 Melem/s 2.1251 Melem/s 2.9325 Melem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 32 threads
time: [12.262 s 18.323 s 24.065 s]
thrpt: [664.86 Kelem/s 873.20 Kelem/s 1.3049 Melem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 48 threads
time: [16.228 s 26.590 s 37.322 s]
thrpt: [643.06 Kelem/s 902.59 Kelem/s 1.4789 Melem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 64 threads
time: [41.926 s 54.231 s 63.360 s]
thrpt: [505.05 Kelem/s 590.07 Kelem/s 763.25 Kelem/s]
scalability가 아직도 안 좋긴 하다.
같은 조건에서 main
(b9ddbed)을 돌렸을 때 결과:
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 1 threads
time: [619.82 ms 629.81 ms 634.21 ms]
thrpt: [788.39 Kelem/s 793.89 Kelem/s 806.68 Kelem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 2 threads
time: [762.19 ms 770.69 ms 776.51 ms]
thrpt: [1.2878 Melem/s 1.2975 Melem/s 1.3120 Melem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 4 threads
time: [899.90 ms 904.88 ms 910.03 ms]
thrpt: [2.1977 Melem/s 2.2102 Melem/s 2.2225 Melem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 8 threads
time: [1.3461 s 1.3542 s 1.3618 s]
thrpt: [2.9372 Melem/s 2.9538 Melem/s 2.9716 Melem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 16 threads
time: [2.5384 s 3.2486 s 4.1637 s]
thrpt: [1.9214 Melem/s 2.4626 Melem/s 3.1516 Melem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 32 threads
time: [20.542 s 24.320 s 27.556 s]
thrpt: [580.64 Kelem/s 657.90 Kelem/s 778.88 Kelem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 48 threads
time: [40.407 s 43.032 s 45.451 s]
thrpt: [528.04 Kelem/s 557.73 Kelem/s 593.96 Kelem/s]
SeqLockAVLTree/Inserted +5e5 SeqlockAVLTree Ops (I: 30%, L: 50%, R: 20%, per: +5e5) by 64 threads
time: [61.687 s 64.075 s 65.964 s]
thrpt: [485.11 Kelem/s 499.42 Kelem/s 518.75 Kelem/s]
코어가 많아지면 소폭 이득을 보는 추세지만, 16코어에서 살짝 성능이 떨어진다는 게 보여진다. 전략을 수정하여 개선해보는 것이 좋을 듯.
Backoff를 제거하고, 일부 쓸모없는 명령어들 지우고, 아마 가장 병목이 컸을 get_height
에서 write_lock 대신 read_lock을 쓰도록 하여, c5n.metal에서 taskset -c 0-17 cargo criterion --bench concurrrent
로 scalability 어느 정도 잘 나오는 것 확인함. 그리고 parent쪽 read lock validation이 왜 필요한가에 대한 연구가 필요하다.
연구해본 결과, recover
함수쪽이 잘못된 것으로 판명되었다. 실제로 lock coupling이 유지가 되려면, node read guard를 validate한 후에, 다시 그 node의 parent read guard도 validate한 지를 해야 하는데 안 했던 것이 이유였다. 따라서, main
에서 move_next
의 node 및 node parent의 read guard를 validate 체크한 것이 위의 recover
의 버그를 해결하는 수가 된 것이었다.
Benchmark: cargo criterion --bench concurrent (only run bench_mixed_seqlockavltree) Envirnment:
The branch seqlockavltree/backoff(5debcd5) result:
Benchmark: cargo criterion --bench concurrnet (only run bench_mixed_seqlockavltree) Envirnment:
The branch seqlockavltree/backoff(5debcd5) result:
Benchmark: cargo criterion --bench concurrnet (only run bench_mixed_seqlockavltree) Envirnment:
The branch seqlockavltree/backoff(5debcd5) result:
Benchmark: cargo criterion --bench concurrnet (only run bench_mixed_seqlockavltree) Envirnment:
The branch seqlockavltree/backoff(5debcd5) result w/o Backoff:
위에 통계들 취합하여 그려보면 다음과 같다. Backoff를 없애는 게, 이득이 큰 듯. 특히, 100% remove에서 차이가 너무 큼
주말에 cursor를 ThreadLocal로 가지고 있을 수 있도록 짜서 다시 벤치마크 돌려볼 것. 특히, ARM 서버(c6g.metal) 대신 NUMA 없이 잘 돌아가는 c5.12xlarge를 중심으로 벤치마킹하며, 성능을 깎아보는 것이 좋아 보인다.
Benchmark: cargo criterion --bench concurrnet (only run bench_mixed_seqlockavltree) Envirnment:
조금씩 수정한 것을 간단히 벤치마킹 돌려봤는데, 유의할만한 성능 변화는 없는 듯하다. 일단, 현재 짠 것이 semantics 상에선 최선이라 생각되기 때문에 이렇게 머지한다. cursor쪽 작업 등의 최적화는 다음 branch에서 하기로 함.
The branch seqlockavltree/backoff(5debcd5, 7117483, b8ab144) result w/o Backoff:
ARM 64코어 머신(AWS EC2: c6g.metal)에서 기존의 SeqLockAVLTree를 벤치마크해본 결과, 16코어 이상에서 많은 throughput 하락이 있었습니다. 이는 아마도 각 operation에서 실패할 경우 loop를 돌 때, backoff가 없어서 생긴 contention으로 추정되기 때문에, 각 operation에 backoff를 추가해봤습니다.