{"id":18683426,"url":"https://github.com/narasimha1997/clfu","last_synced_at":"2025-09-10T18:42:48.085Z","repository":{"id":104766742,"uuid":"549425192","full_name":"Narasimha1997/clfu","owner":"Narasimha1997","description":"Implementation of Constant Time LFU (least frequently used) cache in Go with concurrency safety.","archived":false,"fork":false,"pushed_at":"2022-10-19T12:08:37.000Z","size":36,"stargazers_count":38,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-26T00:03:10.287Z","etag":null,"topics":["algorithm","cache","concurrency","data-structures","go","golang","lfu","lfu-cache"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Narasimha1997.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-11T07:01:28.000Z","updated_at":"2024-01-31T13:13:49.000Z","dependencies_parsed_at":"2023-04-27T13:02:19.741Z","dependency_job_id":null,"html_url":"https://github.com/Narasimha1997/clfu","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Narasimha1997%2Fclfu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Narasimha1997%2Fclfu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Narasimha1997%2Fclfu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Narasimha1997%2Fclfu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Narasimha1997","download_url":"https://codeload.github.com/Narasimha1997/clfu/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248517178,"owners_count":21117408,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithm","cache","concurrency","data-structures","go","golang","lfu","lfu-cache"],"created_at":"2024-11-07T10:14:38.118Z","updated_at":"2025-04-12T04:31:36.323Z","avatar_url":"https://github.com/Narasimha1997.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# clfu\n![Tests](https://github.com/Narasimha1997/clfu/actions/workflows/test.yml/badge.svg)\n[![Go Reference](https://pkg.go.dev/badge/github.com/Narasimha1997/clfu.svg)](https://pkg.go.dev/github.com/Narasimha1997/clfu)\n\nImplementation of Constant Time LFU (least frequently used) cache in Go with concurrency safety. This implementation is based on the paper [An O(1) algorithm for implementing the LFU\ncache eviction scheme](http://dhruvbird.com/lfu.pdf). As opposed to priority heap based LFU cache, this algorithm provides almost O(1) insertion, retrieval and eviction operations by using Linked lists and hash-maps instead of frequency based min-heap data-structure. This algorithm trade-offs memory to improve performance.\n\n## Using the module\nThe codebase can be imported and used as a go-module. To add `clfu` as a go module dependency to your project, run:\n```\ngo get github.com/Narasimha1997/clfu\n```\n\n## Example\nThe example below shows some of the basic operations provided by `clfu`:\n```go\nimport (\n\t\"clfu\"\n\t\"fmt\"\n)\n\nfunc main() {\n\n\t// create a new instance of LFU cache with a max size, you can also use NewLazyLFUCache(size uint, lazyCacheSize uint) to use LFU cache\n    // in lazy mode.\n\tlfuCache := clfu.NewLFUCache(3)\n\n\t// insert values, any interface{} can be used as key, value\n\tlfuCache.Put(\"u939801\", 123, false)\n\tlfuCache.Put(\"u939802\", 411, false)\n\tlfuCache.Put(\"u939803\", 234, false)\n\n\t// insert with replace=true, will replace the value of 'u939802'\n\tlfuCache.Put(\"u939802\", 512, true)\n\n\t// get the current size (should return '3')\n\tfmt.Printf(\"current_size=%d\\n\", lfuCache.CurrentSize())\n\n\t// get the max size (should return '3')\n\tfmt.Printf(\"max_size=%d\\n\", lfuCache.MaxSize())\n\n\t// check if the cache if full\n\tfmt.Printf(\"is_full=%v\\n\", lfuCache.IsFull())\n\n\t// get values (this will increase the frequency of given key 1)\n\trawValue, found := lfuCache.Get(\"u939802\")\n\tif found {\n\t\tfmt.Printf(\"Value of 'u939802' is %d\\n\", (*rawValue).(int))\n\t}\n\n\trawValue, found = lfuCache.Get(\"u939803\")\n\tif found {\n\t\tfmt.Printf(\"Value of 'u939803' is %d\\n\", (*rawValue).(int))\n\t}\n\n\t// insert new entry, should evict `u939801` now  because it is the least used element\n\tlfuCache.Put(\"u939804\", 1000, false)\n\n\t// delete the entry from cache\n\terr := lfuCache.Delete(\"u939804\")\n\tif err != nil {\n\t\tfmt.Printf(\"failed to delete, no key 'u939804'\")\n\t}\n}\n```\n\n### Lazy mode\nAs per the algorithm specification, whenever we call a `Get` on given key, the linked list structure has to be modified to update the frequency of the given key by 1, in normal mode (i.e when lazy mode is disabled) the structural update to linked list is performed on every `Get`, this can be avoided using lazy mode, in lazy mode the frequency updates are not made immediately, instead a slice is used to keep track of the keys whose frequencies needs to be updated, once this slice reaches it's maximum capacity, the linked list is updated in bulk. The bulk update is also triggered when `Put` or `Evict` methods are called to make sure writes always happen on the correct state of the linked list, however the bulk update can also be triggered manually by calling `FlushLazyCounter`. This is how LFU cache can be instantiated in lazy mode:\n```go\n// here 1000 is the size of the LFU cache, 10000 is the size of lazy update slice,\n// i.e bulk update on the linked list will be triggered once after every 10000 gets.\nlfuCache := clfu.NewLFUCache(1000, 10000)\n\n// you can also manually trigger lazy update\nlfuCache.FlushLazyCounter()\n```\nLazy mode is best suitable when `Put` operations are not so frequent and `Get` operations occur in very high volumes.\n\n### Obtaining the cache entries as slice\nThe module provides `AsSlice()` method which can be used to obtain all the elements in LFU cache at that given point in time as a slice, the entries in the returned slice will be in the increasing order of their access frequency.\n```go\nlfuCache := clfu.NewLFUCache(3)\n\n// insert some values\nlfuCache.Put(\"u939801\", 123, false)\nlfuCache.Put(\"u939802\", 411, false)\nlfuCache.Put(\"u939803\", 234, false)\n\n// obtain them as slice\nentries := lfuCache.AsSlice()\nfor _, entry := range *entries {\n\tfmt.Printf(\"Frequency=%d\\n\", entry.Frequency)\n\tfmt.Printf(\"Key=%s\\n\", (*entry.Key).(string))\n\tfmt.Printf(\"Value=%d\\n\", (*entry.Value).(int))\n}\n```\n\nSimilarly we can also use `GetTopFrequencyItems()` to obtain the list entries having highest frequency value and `GetLeastFrequencyItems()` to get the list of entries having least frequency value. \n\n### Testing\nIf you want to make modifications or validate the functions locally run the following command from project root:\n```\ngo test -v\n```\n\nThis will execute the testing suite against the module:\n```\n=== RUN   TestPut\n--- PASS: TestPut (0.00s)\n=== RUN   TestPutWithReplace\n--- PASS: TestPutWithReplace (0.00s)\n=== RUN   TestComplexStructPutAndGet\n--- PASS: TestComplexStructPutAndGet (0.00s)\n=== RUN   TestManualEvict\n--- PASS: TestManualEvict (0.00s)\n=== RUN   TestDelete\n--- PASS: TestDelete (0.00s)\n=== RUN   TestLeastAndFrequentItemsGetter\n--- PASS: TestLeastAndFrequentItemsGetter (0.00s)\n=== RUN   TestMaxSizeResize\n--- PASS: TestMaxSizeResize (0.00s)\n=== RUN   TestConcurrentPut\n--- PASS: TestConcurrentPut (2.84s)\n=== RUN   TestConcurrentGet\n--- PASS: TestConcurrentGet (1.42s)\n=== RUN   TestConcurrentLazyGet\n--- PASS: TestConcurrentLazyGet (1.54s)\nPASS\nok      clfu    5.800s\n```\n\n### Benchmarks\nTo run the benchmark suite, run the following command from the project root:\n```\ngo test -bench=. -benchmem\n```\nThis will run the benchmark suite against the module:\n```\ngoos: linux\ngoarch: amd64\npkg: clfu\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\nBenchmarkPut-12                          3022968               405.9 ns/op           129 B/op          6 allocs/op\nBenchmarkConcurrentPut-12                2232406               522.9 ns/op           129 B/op          6 allocs/op\nBenchmarkGetOperation-12                 7356638               136.7 ns/op            49 B/op          1 allocs/op\nBenchmarkConcurrentGet-12                5105284               212.7 ns/op            51 B/op          1 allocs/op\nBenchmarkLazyLFUGet-12                   7642183               153.8 ns/op            49 B/op          1 allocs/op\nBenchmarkConcurrentLazyLFUGet-12         4828609               217.3 ns/op            51 B/op          1 allocs/op\nPASS\nok      clfu    15.317s\n```\nGoing with the above numbers we can achieve at max `2.46M PUTs` and `7.3M GETs` per second on a single core, in lazy mode we can get max `6.9M GETs`. Note that the LFU cache is suitable for linear operation and not concurrent operations because all the operations need a write lock to be imposed to avoid race conditions. From the benchmarks it is evident that the performance will decrease as we perform concurrent `PUT` and `GET` because of lock-contention. \n\nFrom `examples/main.go` we can also notice that on average, it takes `11.8s` to perform `48M GETs` using concurrency of 12. (tested on 12vCPU machine) and `13.4s` to do the same using lazy mode.\n\n```\nNormal (lazy not enabled): n_goroutines=12, n_access=48000000, time_taken=11.846151472s\nLazy (with size 1000 as cache size): n_goroutines=12, n_access=48000000, time_taken=13.357687219s\n```\n\n### Contributing\nFeel free to raise issues, submit PRs or suggest improvements.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnarasimha1997%2Fclfu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnarasimha1997%2Fclfu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnarasimha1997%2Fclfu/lists"}