From 57f1353efdbb093dfab2469a1c3ada3d9e8b06ed Mon Sep 17 00:00:00 2001 From: Yusuke Kato Date: Sat, 4 May 2019 19:26:04 +0900 Subject: [PATCH] [patch] use native map for performance (#51) Signed-off-by: kpango --- Makefile | 1 + README.md | 194 +++++++++++++++++++++++++------------------------- gache.go | 27 ++++--- gache_test.go | 42 +++++------ go.sum | 5 +- map.go | 190 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 323 insertions(+), 136 deletions(-) create mode 100644 map.go diff --git a/Makefile b/Makefile index 47901b1..885c838 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ all: clean install lint test bench clean: go clean ./... + go clean -modcache rm -rf ./*.log rm -rf ./*.svg rm -rf ./go.* diff --git a/README.md b/README.md index 361034a..dff6386 100644 --- a/README.md +++ b/README.md @@ -49,108 +49,108 @@ go test -count=5 -run=NONE -bench . -benchmem goos: darwin goarch: amd64 pkg: github.com/kpango/gache -BenchmarkGacheWithSmallDataset-8 5000000 291 ns/op 320 B/op 16 allocs/op -BenchmarkGacheWithSmallDataset-8 5000000 295 ns/op 320 B/op 16 allocs/op -BenchmarkGacheWithSmallDataset-8 5000000 298 ns/op 320 B/op 16 allocs/op -BenchmarkGacheWithSmallDataset-8 5000000 294 ns/op 320 B/op 16 allocs/op -BenchmarkGacheWithSmallDataset-8 5000000 299 ns/op 320 B/op 16 allocs/op -BenchmarkGacheWithBigDataset-8 500 3452521 ns/op 799990 B/op 39999 allocs/op -BenchmarkGacheWithBigDataset-8 500 3445126 ns/op 799985 B/op 39999 allocs/op -BenchmarkGacheWithBigDataset-8 500 3444726 ns/op 799991 B/op 39999 allocs/op -BenchmarkGacheWithBigDataset-8 500 3463403 ns/op 799991 B/op 39999 allocs/op -BenchmarkGacheWithBigDataset-8 500 3494554 ns/op 799985 B/op 39999 allocs/op -BenchmarkGocacheWithSmallDataset-8 5000000 340 ns/op 320 B/op 16 allocs/op -BenchmarkGocacheWithSmallDataset-8 5000000 338 ns/op 320 B/op 16 allocs/op +BenchmarkGacheWithSmallDataset-8 10000000 229 ns/op 192 B/op 8 allocs/op +BenchmarkGacheWithSmallDataset-8 10000000 228 ns/op 192 B/op 8 allocs/op +BenchmarkGacheWithSmallDataset-8 10000000 233 ns/op 192 B/op 8 allocs/op +BenchmarkGacheWithSmallDataset-8 10000000 230 ns/op 192 B/op 8 allocs/op +BenchmarkGacheWithSmallDataset-8 10000000 228 ns/op 192 B/op 8 allocs/op +BenchmarkGacheWithBigDataset-8 500 3266326 ns/op 480003 B/op 20000 allocs/op +BenchmarkGacheWithBigDataset-8 500 3435887 ns/op 480003 B/op 20000 allocs/op +BenchmarkGacheWithBigDataset-8 500 3293473 ns/op 480003 B/op 20000 allocs/op +BenchmarkGacheWithBigDataset-8 500 3320686 ns/op 480008 B/op 20000 allocs/op +BenchmarkGacheWithBigDataset-8 500 3306882 ns/op 480003 B/op 20000 allocs/op BenchmarkGocacheWithSmallDataset-8 5000000 342 ns/op 320 B/op 16 allocs/op +BenchmarkGocacheWithSmallDataset-8 5000000 353 ns/op 320 B/op 16 allocs/op +BenchmarkGocacheWithSmallDataset-8 5000000 349 ns/op 320 B/op 16 allocs/op BenchmarkGocacheWithSmallDataset-8 5000000 345 ns/op 320 B/op 16 allocs/op -BenchmarkGocacheWithSmallDataset-8 5000000 342 ns/op 320 B/op 16 allocs/op -BenchmarkGocacheWithBigDataset-8 300 3634880 ns/op 803100 B/op 40047 allocs/op -BenchmarkGocacheWithBigDataset-8 300 3617405 ns/op 804285 B/op 40050 allocs/op -BenchmarkGocacheWithBigDataset-8 300 3728884 ns/op 806466 B/op 40057 allocs/op -BenchmarkGocacheWithBigDataset-8 300 3753736 ns/op 809177 B/op 40092 allocs/op -BenchmarkGocacheWithBigDataset-8 300 3763500 ns/op 808783 B/op 40094 allocs/op -BenchmarkMapWithSmallDataset-8 1000000 1433 ns/op 128 B/op 8 allocs/op -BenchmarkMapWithSmallDataset-8 1000000 1376 ns/op 128 B/op 8 allocs/op -BenchmarkMapWithSmallDataset-8 1000000 1352 ns/op 128 B/op 8 allocs/op -BenchmarkMapWithSmallDataset-8 1000000 1340 ns/op 128 B/op 8 allocs/op -BenchmarkMapWithSmallDataset-8 1000000 1338 ns/op 128 B/op 8 allocs/op -BenchmarkMapWithBigDataset-8 100 10181335 ns/op 332736 B/op 20001 allocs/op -BenchmarkMapWithBigDataset-8 100 10135521 ns/op 332758 B/op 20001 allocs/op -BenchmarkMapWithBigDataset-8 100 10147527 ns/op 332748 B/op 20001 allocs/op -BenchmarkMapWithBigDataset-8 100 10191556 ns/op 332700 B/op 20001 allocs/op -BenchmarkMapWithBigDataset-8 100 10203600 ns/op 332779 B/op 20001 allocs/op -BenchmarkGoCacheWithSmallDataset-8 1000000 1548 ns/op 64 B/op 4 allocs/op -BenchmarkGoCacheWithSmallDataset-8 1000000 1514 ns/op 64 B/op 4 allocs/op -BenchmarkGoCacheWithSmallDataset-8 1000000 1507 ns/op 64 B/op 4 allocs/op -BenchmarkGoCacheWithSmallDataset-8 1000000 1503 ns/op 64 B/op 4 allocs/op -BenchmarkGoCacheWithSmallDataset-8 1000000 1502 ns/op 64 B/op 4 allocs/op -BenchmarkGoCacheWithBigDataset-8 100 10247210 ns/op 175679 B/op 10002 allocs/op -BenchmarkGoCacheWithBigDataset-8 100 10286331 ns/op 175747 B/op 10002 allocs/op -BenchmarkGoCacheWithBigDataset-8 100 10341409 ns/op 175810 B/op 10002 allocs/op -BenchmarkGoCacheWithBigDataset-8 100 10285768 ns/op 175775 B/op 10002 allocs/op -BenchmarkGoCacheWithBigDataset-8 100 10285459 ns/op 175706 B/op 10002 allocs/op -BenchmarkGCacheLRUWithSmallDataset-8 500000 2482 ns/op 320 B/op 16 allocs/op +BenchmarkGocacheWithSmallDataset-8 5000000 344 ns/op 320 B/op 16 allocs/op +BenchmarkGocacheWithBigDataset-8 500 3769988 ns/op 803860 B/op 40049 allocs/op +BenchmarkGocacheWithBigDataset-8 500 3662657 ns/op 804242 B/op 40052 allocs/op +BenchmarkGocacheWithBigDataset-8 300 3693801 ns/op 807375 B/op 40087 allocs/op +BenchmarkGocacheWithBigDataset-8 300 3662074 ns/op 806655 B/op 40083 allocs/op +BenchmarkGocacheWithBigDataset-8 500 3682411 ns/op 804629 B/op 40052 allocs/op +BenchmarkMapWithSmallDataset-8 1000000 1255 ns/op 128 B/op 8 allocs/op +BenchmarkMapWithSmallDataset-8 1000000 1264 ns/op 128 B/op 8 allocs/op +BenchmarkMapWithSmallDataset-8 1000000 1301 ns/op 128 B/op 8 allocs/op +BenchmarkMapWithSmallDataset-8 1000000 1265 ns/op 128 B/op 8 allocs/op +BenchmarkMapWithSmallDataset-8 1000000 1269 ns/op 128 B/op 8 allocs/op +BenchmarkMapWithBigDataset-8 100 10038787 ns/op 332771 B/op 20001 allocs/op +BenchmarkMapWithBigDataset-8 200 9950130 ns/op 326347 B/op 20000 allocs/op +BenchmarkMapWithBigDataset-8 200 10025711 ns/op 326395 B/op 20000 allocs/op +BenchmarkMapWithBigDataset-8 200 9913849 ns/op 326375 B/op 20000 allocs/op +BenchmarkMapWithBigDataset-8 100 10089161 ns/op 332739 B/op 20001 allocs/op +BenchmarkGoCacheWithSmallDataset-8 1000000 1488 ns/op 64 B/op 4 allocs/op +BenchmarkGoCacheWithSmallDataset-8 1000000 1493 ns/op 64 B/op 4 allocs/op +BenchmarkGoCacheWithSmallDataset-8 1000000 1500 ns/op 64 B/op 4 allocs/op +BenchmarkGoCacheWithSmallDataset-8 1000000 1524 ns/op 64 B/op 4 allocs/op +BenchmarkGoCacheWithSmallDataset-8 1000000 1496 ns/op 64 B/op 4 allocs/op +BenchmarkGoCacheWithBigDataset-8 100 10172852 ns/op 175826 B/op 10003 allocs/op +BenchmarkGoCacheWithBigDataset-8 100 10196606 ns/op 175719 B/op 10002 allocs/op +BenchmarkGoCacheWithBigDataset-8 100 10155225 ns/op 175730 B/op 10002 allocs/op +BenchmarkGoCacheWithBigDataset-8 100 10080347 ns/op 175687 B/op 10002 allocs/op +BenchmarkGoCacheWithBigDataset-8 100 10139212 ns/op 175739 B/op 10002 allocs/op +BenchmarkGCacheLRUWithSmallDataset-8 500000 2447 ns/op 320 B/op 16 allocs/op +BenchmarkGCacheLRUWithSmallDataset-8 500000 2481 ns/op 320 B/op 16 allocs/op BenchmarkGCacheLRUWithSmallDataset-8 500000 2463 ns/op 320 B/op 16 allocs/op -BenchmarkGCacheLRUWithSmallDataset-8 500000 2506 ns/op 320 B/op 16 allocs/op -BenchmarkGCacheLRUWithSmallDataset-8 500000 2505 ns/op 320 B/op 16 allocs/op -BenchmarkGCacheLRUWithSmallDataset-8 500000 2518 ns/op 320 B/op 16 allocs/op -BenchmarkGCacheLRUWithBigDataset-8 100 21187149 ns/op 1976314 B/op 60152 allocs/op -BenchmarkGCacheLRUWithBigDataset-8 100 21365157 ns/op 1974912 B/op 60146 allocs/op -BenchmarkGCacheLRUWithBigDataset-8 100 21295620 ns/op 1971798 B/op 60136 allocs/op -BenchmarkGCacheLRUWithBigDataset-8 100 20833879 ns/op 1975621 B/op 60147 allocs/op -BenchmarkGCacheLRUWithBigDataset-8 100 20802686 ns/op 1976400 B/op 60151 allocs/op -BenchmarkGCacheLFUWithSmallDataset-8 500000 3081 ns/op 512 B/op 20 allocs/op -BenchmarkGCacheLFUWithSmallDataset-8 500000 3023 ns/op 512 B/op 20 allocs/op -BenchmarkGCacheLFUWithSmallDataset-8 500000 2918 ns/op 512 B/op 20 allocs/op -BenchmarkGCacheLFUWithSmallDataset-8 500000 2928 ns/op 512 B/op 20 allocs/op -BenchmarkGCacheLFUWithSmallDataset-8 500000 2894 ns/op 512 B/op 20 allocs/op -BenchmarkGCacheLFUWithBigDataset-8 100 21955102 ns/op 1439551 B/op 49986 allocs/op -BenchmarkGCacheLFUWithBigDataset-8 100 21821424 ns/op 1440684 B/op 49992 allocs/op -BenchmarkGCacheLFUWithBigDataset-8 100 21668914 ns/op 1439789 B/op 49987 allocs/op -BenchmarkGCacheLFUWithBigDataset-8 100 21698323 ns/op 1440822 B/op 49993 allocs/op -BenchmarkGCacheLFUWithBigDataset-8 100 21813723 ns/op 1440316 B/op 49990 allocs/op -BenchmarkGCacheARCWithSmallDataset-8 500000 3198 ns/op 320 B/op 16 allocs/op -BenchmarkGCacheARCWithSmallDataset-8 500000 3069 ns/op 320 B/op 16 allocs/op -BenchmarkGCacheARCWithSmallDataset-8 500000 3090 ns/op 320 B/op 16 allocs/op -BenchmarkGCacheARCWithSmallDataset-8 500000 3095 ns/op 320 B/op 16 allocs/op -BenchmarkGCacheARCWithSmallDataset-8 500000 3076 ns/op 320 B/op 16 allocs/op -BenchmarkGCacheARCWithBigDataset-8 30 61898429 ns/op 3002668 B/op 80286 allocs/op -BenchmarkGCacheARCWithBigDataset-8 30 63439372 ns/op 2984606 B/op 80233 allocs/op -BenchmarkGCacheARCWithBigDataset-8 30 62344244 ns/op 3001549 B/op 80292 allocs/op -BenchmarkGCacheARCWithBigDataset-8 30 63272118 ns/op 2991984 B/op 80250 allocs/op -BenchmarkGCacheARCWithBigDataset-8 30 62868651 ns/op 3012101 B/op 80355 allocs/op -BenchmarkFreeCacheWithSmallDataset-8 1000000 1221 ns/op 26 B/op 4 allocs/op +BenchmarkGCacheLRUWithSmallDataset-8 500000 2458 ns/op 320 B/op 16 allocs/op +BenchmarkGCacheLRUWithSmallDataset-8 500000 2449 ns/op 320 B/op 16 allocs/op +BenchmarkGCacheLRUWithBigDataset-8 100 20303907 ns/op 1978406 B/op 60159 allocs/op +BenchmarkGCacheLRUWithBigDataset-8 100 20252786 ns/op 1977566 B/op 60158 allocs/op +BenchmarkGCacheLRUWithBigDataset-8 100 20340804 ns/op 1975780 B/op 60155 allocs/op +BenchmarkGCacheLRUWithBigDataset-8 100 20360024 ns/op 1974747 B/op 60147 allocs/op +BenchmarkGCacheLRUWithBigDataset-8 100 21916110 ns/op 1973632 B/op 60146 allocs/op +BenchmarkGCacheLFUWithSmallDataset-8 500000 3159 ns/op 512 B/op 20 allocs/op +BenchmarkGCacheLFUWithSmallDataset-8 500000 3058 ns/op 512 B/op 20 allocs/op +BenchmarkGCacheLFUWithSmallDataset-8 500000 3345 ns/op 512 B/op 20 allocs/op +BenchmarkGCacheLFUWithSmallDataset-8 500000 3605 ns/op 512 B/op 20 allocs/op +BenchmarkGCacheLFUWithSmallDataset-8 500000 3095 ns/op 512 B/op 20 allocs/op +BenchmarkGCacheLFUWithBigDataset-8 50 23091335 ns/op 1439551 B/op 49988 allocs/op +BenchmarkGCacheLFUWithBigDataset-8 100 21456468 ns/op 1439454 B/op 49986 allocs/op +BenchmarkGCacheLFUWithBigDataset-8 100 22155062 ns/op 1439969 B/op 49989 allocs/op +BenchmarkGCacheLFUWithBigDataset-8 100 22007458 ns/op 1439730 B/op 49987 allocs/op +BenchmarkGCacheLFUWithBigDataset-8 100 23379462 ns/op 1440896 B/op 49993 allocs/op +BenchmarkGCacheARCWithSmallDataset-8 500000 3778 ns/op 320 B/op 16 allocs/op +BenchmarkGCacheARCWithSmallDataset-8 300000 3550 ns/op 320 B/op 16 allocs/op +BenchmarkGCacheARCWithSmallDataset-8 500000 3144 ns/op 320 B/op 16 allocs/op +BenchmarkGCacheARCWithSmallDataset-8 500000 3208 ns/op 320 B/op 16 allocs/op +BenchmarkGCacheARCWithSmallDataset-8 500000 3124 ns/op 320 B/op 16 allocs/op +BenchmarkGCacheARCWithBigDataset-8 30 65356559 ns/op 2986013 B/op 80239 allocs/op +BenchmarkGCacheARCWithBigDataset-8 30 68426599 ns/op 2997542 B/op 80283 allocs/op +BenchmarkGCacheARCWithBigDataset-8 30 67212658 ns/op 2997620 B/op 80287 allocs/op +BenchmarkGCacheARCWithBigDataset-8 20 67104716 ns/op 2992918 B/op 80253 allocs/op +BenchmarkGCacheARCWithBigDataset-8 30 66066147 ns/op 2996510 B/op 80281 allocs/op BenchmarkFreeCacheWithSmallDataset-8 1000000 1216 ns/op 26 B/op 4 allocs/op -BenchmarkFreeCacheWithSmallDataset-8 1000000 1212 ns/op 26 B/op 4 allocs/op -BenchmarkFreeCacheWithSmallDataset-8 1000000 1215 ns/op 26 B/op 4 allocs/op -BenchmarkFreeCacheWithSmallDataset-8 1000000 1212 ns/op 26 B/op 4 allocs/op -BenchmarkFreeCacheWithBigDataset-8 100 19999670 ns/op 126810752 B/op 39937 allocs/op -BenchmarkFreeCacheWithBigDataset-8 100 19969249 ns/op 126810741 B/op 39937 allocs/op -BenchmarkFreeCacheWithBigDataset-8 100 20030303 ns/op 126810746 B/op 39937 allocs/op -BenchmarkFreeCacheWithBigDataset-8 100 20055135 ns/op 126810783 B/op 39937 allocs/op -BenchmarkFreeCacheWithBigDataset-8 100 20039316 ns/op 126810721 B/op 39937 allocs/op -BenchmarkBigCacheWithSmallDataset-8 1000000 1409 ns/op 418 B/op 8 allocs/op -BenchmarkBigCacheWithSmallDataset-8 1000000 1303 ns/op 418 B/op 8 allocs/op -BenchmarkBigCacheWithSmallDataset-8 1000000 1281 ns/op 418 B/op 8 allocs/op -BenchmarkBigCacheWithSmallDataset-8 1000000 1322 ns/op 418 B/op 8 allocs/op -BenchmarkBigCacheWithSmallDataset-8 1000000 1282 ns/op 418 B/op 8 allocs/op -BenchmarkBigCacheWithBigDataset-8 20 64710802 ns/op 218923337 B/op 30322 allocs/op -BenchmarkBigCacheWithBigDataset-8 30 81748764 ns/op 228873497 B/op 30236 allocs/op -BenchmarkBigCacheWithBigDataset-8 30 60661622 ns/op 228847402 B/op 30232 allocs/op -BenchmarkBigCacheWithBigDataset-8 30 47265941 ns/op 228855801 B/op 30232 allocs/op -BenchmarkBigCacheWithBigDataset-8 30 43173717 ns/op 228862094 B/op 30234 allocs/op -BenchmarkMCacheWithSmallDataset-8 200000 13406 ns/op 4385 B/op 40 allocs/op -BenchmarkMCacheWithSmallDataset-8 200000 21958 ns/op 4380 B/op 40 allocs/op -BenchmarkMCacheWithSmallDataset-8 100000 30215 ns/op 4393 B/op 40 allocs/op -BenchmarkMCacheWithSmallDataset-8 100000 22808 ns/op 2102 B/op 31 allocs/op -BenchmarkMCacheWithSmallDataset-8 100000 19290 ns/op 2496 B/op 36 allocs/op -BenchmarkMCacheWithBigDataset-8 20 79083650 ns/op 6239998 B/op 90001 allocs/op -BenchmarkMCacheWithBigDataset-8 30 76086004 ns/op 6240040 B/op 90001 allocs/op -BenchmarkMCacheWithBigDataset-8 30 106160223 ns/op 6240044 B/op 90001 allocs/op -BenchmarkMCacheWithBigDataset-8 30 49614440 ns/op 5966882 B/op 85998 allocs/op -BenchmarkMCacheWithBigDataset-8 30 51878904 ns/op 5781447 B/op 85224 allocs/op +BenchmarkFreeCacheWithSmallDataset-8 1000000 1245 ns/op 26 B/op 4 allocs/op +BenchmarkFreeCacheWithSmallDataset-8 1000000 1249 ns/op 26 B/op 4 allocs/op +BenchmarkFreeCacheWithSmallDataset-8 1000000 1297 ns/op 26 B/op 4 allocs/op +BenchmarkFreeCacheWithSmallDataset-8 1000000 1291 ns/op 26 B/op 4 allocs/op +BenchmarkFreeCacheWithBigDataset-8 50 26189739 ns/op 126835570 B/op 39940 allocs/op +BenchmarkFreeCacheWithBigDataset-8 50 22649865 ns/op 126835572 B/op 39940 allocs/op +BenchmarkFreeCacheWithBigDataset-8 100 22460533 ns/op 126810825 B/op 39937 allocs/op +BenchmarkFreeCacheWithBigDataset-8 100 22407424 ns/op 126810837 B/op 39937 allocs/op +BenchmarkFreeCacheWithBigDataset-8 100 22559765 ns/op 126810840 B/op 39937 allocs/op +BenchmarkBigCacheWithSmallDataset-8 1000000 1419 ns/op 418 B/op 8 allocs/op +BenchmarkBigCacheWithSmallDataset-8 1000000 1314 ns/op 418 B/op 8 allocs/op +BenchmarkBigCacheWithSmallDataset-8 1000000 1369 ns/op 418 B/op 8 allocs/op +BenchmarkBigCacheWithSmallDataset-8 1000000 1412 ns/op 418 B/op 8 allocs/op +BenchmarkBigCacheWithSmallDataset-8 1000000 1342 ns/op 418 B/op 8 allocs/op +BenchmarkBigCacheWithBigDataset-8 20 67048752 ns/op 218980016 B/op 30322 allocs/op +BenchmarkBigCacheWithBigDataset-8 20 58592116 ns/op 218988724 B/op 30321 allocs/op +BenchmarkBigCacheWithBigDataset-8 30 49927797 ns/op 229230211 B/op 30234 allocs/op +BenchmarkBigCacheWithBigDataset-8 30 61055157 ns/op 229220825 B/op 30233 allocs/op +BenchmarkBigCacheWithBigDataset-8 20 57389927 ns/op 219010363 B/op 30323 allocs/op +BenchmarkMCacheWithSmallDataset-8 200000 16411 ns/op 4379 B/op 40 allocs/op +BenchmarkMCacheWithSmallDataset-8 100000 12582 ns/op 4394 B/op 40 allocs/op +BenchmarkMCacheWithSmallDataset-8 100000 22546 ns/op 4361 B/op 40 allocs/op +BenchmarkMCacheWithSmallDataset-8 100000 20408 ns/op 4405 B/op 40 allocs/op +BenchmarkMCacheWithSmallDataset-8 100000 26556 ns/op 4462 B/op 40 allocs/op +BenchmarkMCacheWithBigDataset-8 20 56199562 ns/op 4320073 B/op 70002 allocs/op +BenchmarkMCacheWithBigDataset-8 30 47394147 ns/op 4670813 B/op 73655 allocs/op +BenchmarkMCacheWithBigDataset-8 50 51949655 ns/op 6237569 B/op 89975 allocs/op +BenchmarkMCacheWithBigDataset-8 50 50295095 ns/op 4739066 B/op 74366 allocs/op +BenchmarkMCacheWithBigDataset-8 50 55176820 ns/op 6239356 B/op 89994 allocs/op PASS -ok github.com/kpango/gache 249.928s +ok github.com/kpango/gache 232.025s ``` ## Contribution diff --git a/gache.go b/gache.go index 835a841..fa3e660 100644 --- a/gache.go +++ b/gache.go @@ -55,7 +55,6 @@ type ( // func (g *gache)SetIfNotExists(string, interface{}){} // func SetWithExpireIfNotExists(string, interface{}, time.Duration){} // func (g *gache)SetWithExpireIfNotExists(string, interface{}, time.Duration){} - } // gache is base instance type @@ -66,7 +65,7 @@ type ( expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } value struct { @@ -102,7 +101,7 @@ func newGache() *gache { expire: int64(time.Second * 30), } for i := range g.shards { - g.shards[i] = new(sync.Map) + g.shards[i] = new(Map) } g.expChan = make(chan string, len(g.shards)*10) return g @@ -226,8 +225,8 @@ func (g *gache) get(key string) (interface{}, int64, bool) { return nil, 0, false } - if d := v.(value); d.isValid() { - return d.val, d.expire, true + if v.isValid() { + return v.val, v.expire, true } g.expiration(key) @@ -291,7 +290,7 @@ func Set(key string, val interface{}) { // Delete deletes value from Gache using key func (g *gache) Delete(key string) { for { - if v := atomic.LoadUint64((*uint64)(&g.l)); atomic.CompareAndSwapUint64((*uint64)(&g.l), v, v-1) { + if v := atomic.LoadUint64(&g.l); atomic.CompareAndSwapUint64(&g.l, v, v-1) { g.shards[xxhash.Sum64(*(*[]byte)(unsafe.Pointer(&key)))&mask].Delete(key) return } @@ -320,13 +319,13 @@ func (g *gache) DeleteExpired(ctx context.Context) uint64 { for i := range g.shards { wg.Add(1) go func(c context.Context, idx int) { - g.shards[idx].Range(func(k, v interface{}) bool { + g.shards[idx].Range(func(k string, v value) bool { select { case <-c.Done(): return false default: - if d := v.(value); !d.isValid() { - g.expiration(k.(string)) + if !v.isValid() { + g.expiration(k) atomic.AddUint64(&rows, 1) } return true @@ -350,15 +349,15 @@ func (g *gache) Foreach(ctx context.Context, f func(string, interface{}, int64) for i := range g.shards { wg.Add(1) go func(c context.Context, idx int) { - g.shards[idx].Range(func(k, v interface{}) bool { + g.shards[idx].Range(func(k string, v value) bool { select { case <-c.Done(): return false default: - if d := v.(value); d.isValid() { - return f(k.(string), d.val, d.expire) + if v.isValid() { + return f(k, v.val, v.expire) } - g.expiration(k.(string)) + g.expiration(k) return true } }) @@ -431,7 +430,7 @@ func Read(r io.Reader) error { // Clear deletes all key and value present in the Gache. func (g *gache) Clear() { for i := range g.shards { - g.shards[i] = new(sync.Map) + g.shards[i] = new(Map) } } diff --git a/gache_test.go b/gache_test.go index 879abf1..cc5210a 100644 --- a/gache_test.go +++ b/gache_test.go @@ -93,7 +93,7 @@ func Test_gache_SetDefaultExpire(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { ex time.Duration @@ -152,7 +152,7 @@ func Test_gache_EnableExpiredHook(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } tests := []struct { name string @@ -203,7 +203,7 @@ func Test_gache_DisableExpiredHook(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } tests := []struct { name string @@ -254,7 +254,7 @@ func Test_gache_SetExpiredHook(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { f func(context.Context, string) @@ -313,7 +313,7 @@ func Test_gache_StartExpired(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { ctx context.Context @@ -353,7 +353,7 @@ func Test_gache_ToMap(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { ctx context.Context @@ -412,7 +412,7 @@ func Test_gache_ToRawMap(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { ctx context.Context @@ -471,7 +471,7 @@ func Test_gache_get(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { key string @@ -519,7 +519,7 @@ func Test_gache_Get(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { key string @@ -588,7 +588,7 @@ func Test_gache_GetWithExpire(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { key string @@ -665,7 +665,7 @@ func Test_gache_set(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { key string @@ -703,7 +703,7 @@ func Test_gache_SetWithExpire(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { key string @@ -760,7 +760,7 @@ func Test_gache_Set(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { key string @@ -815,7 +815,7 @@ func Test_gache_Delete(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { key string @@ -868,7 +868,7 @@ func Test_gache_expiration(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { key string @@ -904,7 +904,7 @@ func Test_gache_DeleteExpired(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { ctx context.Context @@ -963,7 +963,7 @@ func Test_gache_Foreach(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { ctx context.Context @@ -1040,7 +1040,7 @@ func Test_gache_Len(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } tests := []struct { name string @@ -1075,7 +1075,7 @@ func Test_gache_Write(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { ctx context.Context @@ -1146,7 +1146,7 @@ func Test_gache_Read(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } type args struct { r io.Reader @@ -1205,7 +1205,7 @@ func Test_gache_Clear(t *testing.T) { expGroup singleflight.Group expire int64 l uint64 - shards [slen]*sync.Map + shards [slen]*Map } tests := []struct { name string diff --git a/go.sum b/go.sum index 984d25a..30dcbfc 100644 --- a/go.sum +++ b/go.sum @@ -22,13 +22,10 @@ github.com/hlts2/gocache v0.0.0-20181007125314-e9a99e525ba1/go.mod h1:u/v6wO8kS5 github.com/hlts2/gocache v0.0.0-20190217073200-8b772e486b6e h1:DbBkW74dsFK7w8ggFtenRoBriAGejhhDFGVfvDdpG5o= github.com/hlts2/gocache v0.0.0-20190217073200-8b772e486b6e/go.mod h1:F4tUovaw56AzbV8K7ET39ZhQLFP8c8bLXRIuVvHAHUg= github.com/kpango/fastime v1.0.0/go.mod h1:Y5XY5bLG5yc7g2XmMUzc22XYV1XaH+KgUOHkDvLp4SA= -github.com/kpango/fastime v1.0.8 h1:Wif5eocdsIXmMG+8HHfRP/jD6UUl+/OVTJ+sMzvA1+E= -github.com/kpango/fastime v1.0.8/go.mod h1:Y5XY5bLG5yc7g2XmMUzc22XYV1XaH+KgUOHkDvLp4SA= github.com/kpango/fastime v1.0.9 h1:GZ7WtFFpTCq8aPXAM+AxKZ1MtNYc22fTpEp4tPKuiKI= github.com/kpango/fastime v1.0.9/go.mod h1:lVqUTcXmQnk1wriyvq5DElbRSRDC0XtqbXQRdz0Eo+g= github.com/kpango/gache v1.1.0/go.mod h1:BHKRCYnJ2pRFFIJNc61KTJb3KXSzlrt/ITfgfCQJAJw= -github.com/kpango/glg v1.3.2 h1:daEbLGtRspX5MYne4ppkl2StRN/hc8hE+ntnu38tsOI= -github.com/kpango/glg v1.3.2/go.mod h1:7zzaAoMqvngad+sagWLjr00EQMJaqyGONdg0WYBAO3M= +github.com/kpango/glg v1.3.3 h1:SUPtVHiB1jWNJ+v0CGjW0O1bTQzBji19sq034gfWMHE= github.com/kpango/glg v1.3.3/go.mod h1:YM6wQXx2ktVPw7qf5UQUg2y29lub0KZ46L3zI3O1IiA= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= diff --git a/map.go b/map.go new file mode 100644 index 0000000..566d857 --- /dev/null +++ b/map.go @@ -0,0 +1,190 @@ +package gache + +import ( + "sync" + "sync/atomic" + "unsafe" +) + +type Map struct { + mu sync.Mutex + read atomic.Value + dirty map[string]*entryMap + misses int +} + +type readOnlyMap struct { + m map[string]*entryMap + amended bool +} + +var expungedMap = unsafe.Pointer(new(value)) + +type entryMap struct { + p unsafe.Pointer +} + +func newEntryMap(i value) *entryMap { + return &entryMap{p: unsafe.Pointer(&i)} +} + +func (m *Map) Load(key string) (value value, ok bool) { + read, _ := m.read.Load().(readOnlyMap) + e, ok := read.m[key] + if !ok && read.amended { + m.mu.Lock() + read, _ = m.read.Load().(readOnlyMap) + e, ok = read.m[key] + if !ok && read.amended { + e, ok = m.dirty[key] + m.missLocked() + } + m.mu.Unlock() + } + if !ok { + return value, false + } + return e.load() +} + +func (e *entryMap) load() (val value, ok bool) { + p := atomic.LoadPointer(&e.p) + if p == nil || p == expungedMap { + return val, false + } + return *(*value)(p), true +} + +func (m *Map) Store(key string, val value) { + read, _ := m.read.Load().(readOnlyMap) + if e, ok := read.m[key]; ok && e.tryStore(&val) { + return + } + + m.mu.Lock() + read, _ = m.read.Load().(readOnlyMap) + if e, ok := read.m[key]; ok { + if e.unexpungeLocked() { + m.dirty[key] = e + } + e.storeLocked(&val) + } else if e, ok := m.dirty[key]; ok { + e.storeLocked(&val) + } else { + if !read.amended { + m.dirtyLocked() + m.read.Store(readOnlyMap{m: read.m, amended: true}) + } + m.dirty[key] = newEntryMap(val) + } + m.mu.Unlock() +} + +func (e *entryMap) tryStore(i *value) bool { + for { + p := atomic.LoadPointer(&e.p) + if p == expungedMap { + return false + } + if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { + return true + } + } +} + +func (e *entryMap) unexpungeLocked() (wasExpunged bool) { + return atomic.CompareAndSwapPointer(&e.p, expungedMap, nil) +} + +func (e *entryMap) storeLocked(i *value) { + atomic.StorePointer(&e.p, unsafe.Pointer(i)) +} + +func (m *Map) Delete(key string) { + read, _ := m.read.Load().(readOnlyMap) + e, ok := read.m[key] + if !ok && read.amended { + m.mu.Lock() + read, _ = m.read.Load().(readOnlyMap) + e, ok = read.m[key] + if !ok && read.amended { + delete(m.dirty, key) + } + m.mu.Unlock() + } + if ok { + e.delete() + } +} + +func (e *entryMap) delete() (hadValue bool) { + for { + p := atomic.LoadPointer(&e.p) + if p == nil || p == expungedMap { + return false + } + if atomic.CompareAndSwapPointer(&e.p, p, nil) { + return true + } + } +} + +func (m *Map) Range(f func(key string, value value) bool) { + read, _ := m.read.Load().(readOnlyMap) + if read.amended { + m.mu.Lock() + read, _ = m.read.Load().(readOnlyMap) + if read.amended { + read = readOnlyMap{m: m.dirty} + m.read.Store(read) + m.dirty = nil + m.misses = 0 + } + m.mu.Unlock() + } + + for k, e := range read.m { + v, ok := e.load() + if !ok { + continue + } + if !f(k, v) { + break + } + } +} + +func (m *Map) missLocked() { + m.misses++ + if m.misses < len(m.dirty) { + return + } + m.read.Store(readOnlyMap{m: m.dirty}) + m.dirty = nil + m.misses = 0 +} + +func (m *Map) dirtyLocked() { + if m.dirty != nil { + return + } + + read, _ := m.read.Load().(readOnlyMap) + m.dirty = make(map[string]*entryMap, len(read.m)) + for k, e := range read.m { + if !e.tryExpungeLocked() { + m.dirty[k] = e + } + } +} + +func (e *entryMap) tryExpungeLocked() (isExpunged bool) { + p := atomic.LoadPointer(&e.p) + for p == nil { + if atomic.CompareAndSwapPointer(&e.p, nil, expungedMap) { + return true + } + p = atomic.LoadPointer(&e.p) + } + return p == expungedMap +}