{"id":13609988,"url":"https://github.com/alphadose/ZenQ","last_synced_at":"2025-04-12T22:32:24.425Z","repository":{"id":37478969,"uuid":"486161718","full_name":"alphadose/ZenQ","owner":"alphadose","description":"A thread-safe queue faster and more resource efficient than golang's native channels","archived":false,"fork":false,"pushed_at":"2024-03-12T06:55:02.000Z","size":215,"stargazers_count":656,"open_issues_count":6,"forks_count":18,"subscribers_count":12,"default_branch":"main","last_synced_at":"2024-10-11T15:16:56.933Z","etag":null,"topics":["concurrency","fastest","go","golang","high-throughput","highly-concurrent","lock-free","low-latency","memory-efficient","mpsc-queue","optimization","ringbuffer","spsc-queue","thread-safe","zenq","zero-allocations"],"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/alphadose.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"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-04-27T11:19:57.000Z","updated_at":"2024-10-02T19:05:35.000Z","dependencies_parsed_at":"2024-03-12T07:54:24.017Z","dependency_job_id":null,"html_url":"https://github.com/alphadose/ZenQ","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alphadose%2FZenQ","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alphadose%2FZenQ/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alphadose%2FZenQ/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alphadose%2FZenQ/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alphadose","download_url":"https://codeload.github.com/alphadose/ZenQ/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248641084,"owners_count":21138139,"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":["concurrency","fastest","go","golang","high-throughput","highly-concurrent","lock-free","low-latency","memory-efficient","mpsc-queue","optimization","ringbuffer","spsc-queue","thread-safe","zenq","zero-allocations"],"created_at":"2024-08-01T19:01:40.024Z","updated_at":"2025-04-12T22:32:24.100Z","avatar_url":"https://github.com/alphadose.png","language":"Go","funding_links":[],"categories":["Go","Repositories"],"sub_categories":[],"readme":"# ZenQ\n\n\u003e A low-latency thread-safe queue in golang implemented using a lock-free ringbuffer and runtime internals\n\nBased on the [LMAX Disruptor Pattern](https://lmax-exchange.github.io/disruptor/disruptor.html)\n\n## Features\n\n* Much faster than native channels in both SPSC (single-producer-single-consumer) and MPSC (multi-producer-single-consumer) modes in terms of `time/op`\n* More resource efficient in terms of `memory_allocation/op` and `num_allocations/op` evident while benchmarking large batch size inputs\n* Handles the case where NUM_WRITER_GOROUTINES \u003e NUM_CPU_CORES much better than native channels\n* Selection from multiple ZenQs just like golang's `select{}` ensuring fair selection and no starvation\n* Closing a ZenQ\n\nBenchmarks to support the above claims [here](#benchmarks)\n\n## Installation\n\nYou need Golang [1.19.x](https://go.dev/dl/) or above\n\n```bash\n$ go get github.com/alphadose/zenq/v2\n```\n\n## Usage\n\n1. Simple Read/Write\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/alphadose/zenq/v2\"\n)\n\ntype payload struct {\n\talpha int\n\tbeta  string\n}\n\nfunc main() {\n\tzq := zenq.New[payload](10)\n\n\tfor j := 0; j \u003c 5; j++ {\n\t\tgo func() {\n\t\t\tfor i := 0; i \u003c 20; i++ {\n\t\t\t\tzq.Write(payload{\n\t\t\t\t\talpha: i,\n\t\t\t\t\tbeta:  fmt.Sprint(i),\n\t\t\t\t})\n\t\t\t}\n\t\t}()\n\t}\n\n\tfor i := 0; i \u003c 100; i++ {\n\t\tif data, queueOpen := zq.Read(); queueOpen {\n\t\t\tfmt.Printf(\"%+v\\n\", data)\n\t\t}\n\t}\n}\n```\n\n2. **Selection** from multiple ZenQs just like golang's native `select{}`. The selection process is fair i.e no single ZenQ gets starved\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/alphadose/zenq/v2\"\n)\n\ntype custom1 struct {\n\talpha int\n\tbeta  string\n}\n\ntype custom2 struct {\n\tgamma int\n}\n\nconst size = 100\n\nvar (\n\tzq1 = zenq.New[int](size)\n\tzq2 = zenq.New[string](size)\n\tzq3 = zenq.New[custom1](size)\n\tzq4 = zenq.New[*custom2](size)\n)\n\nfunc main() {\n\tgo looper(intProducer)\n\tgo looper(stringProducer)\n\tgo looper(custom1Producer)\n\tgo looper(custom2Producer)\n\n\tfor i := 0; i \u003c 40; i++ {\n\n\t\t// Selection occurs here\n\t\tif data := zenq.Select(zq1, zq2, zq3, zq4); data != nil {\n\t\t\tswitch data.(type) {\n\t\t\tcase int:\n\t\t\t\tfmt.Printf(\"Received int %d\\n\", data)\n\t\t\tcase string:\n\t\t\t\tfmt.Printf(\"Received string %s\\n\", data)\n\t\t\tcase custom1:\n\t\t\t\tfmt.Printf(\"Received custom data type number 1 %#v\\n\", data)\n\t\t\tcase *custom2:\n\t\t\t\tfmt.Printf(\"Received pointer %#v\\n\", data)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc intProducer(ctr int) { zq1.Write(ctr) }\n\nfunc stringProducer(ctr int) { zq2.Write(fmt.Sprint(ctr * 10)) }\n\nfunc custom1Producer(ctr int) { zq3.Write(custom1{alpha: ctr, beta: fmt.Sprint(ctr)}) }\n\nfunc custom2Producer(ctr int) { zq4.Write(\u0026custom2{gamma: 1 \u003c\u003c ctr}) }\n\nfunc looper(producer func(ctr int)) {\n\tfor i := 0; i \u003c 10; i++ {\n\t\tproducer(i)\n\t}\n}\n```\n\n## Benchmarks\n\nBenchmarking code available [here](./benchmarks)\n\nNote that if you run the benchmarks with `--race` flag then ZenQ will perform slower because the `--race` flag slows\ndown the atomic operations in golang. Under normal circumstances, ZenQ will outperform golang native channels.\n\n### Hardware Specs\n\n```\n❯ neofetch\n                    'c.          alphadose@ReiEki.local\n                 ,xNMM.          ----------------------\n               .OMMMMo           OS: macOS 12.3 21E230 arm64\n               OMMM0,            Host: MacBookAir10,1\n     .;loddo:' loolloddol;.      Kernel: 21.4.0\n   cKMMMMMMMMMMNWMMMMMMMMMM0:    Uptime: 6 hours, 41 mins\n .KMMMMMMMMMMMMMMMMMMMMMMMWd.    Packages: 86 (brew)\n XMMMMMMMMMMMMMMMMMMMMMMMX.      Shell: zsh 5.8\n;MMMMMMMMMMMMMMMMMMMMMMMM:       Resolution: 1440x900\n:MMMMMMMMMMMMMMMMMMMMMMMM:       DE: Aqua\n.MMMMMMMMMMMMMMMMMMMMMMMMX.      WM: Rectangle\n kMMMMMMMMMMMMMMMMMMMMMMMMWd.    Terminal: iTerm2\n .XMMMMMMMMMMMMMMMMMMMMMMMMMMk   Terminal Font: FiraCodeNerdFontComplete-Medium 16 (normal)\n  .XMMMMMMMMMMMMMMMMMMMMMMMMK.   CPU: Apple M1\n    kMMMMMMMMMMMMMMMMMMMMMMd     GPU: Apple M1\n     ;KMMMMMMMWXXWMMMMMMMk.      Memory: 1370MiB / 8192MiB\n       .cooc,.    .,coo:.\n\n```\n\n### Terminology\n\n* NUM_WRITERS -\u003e The number of goroutines concurrently writing to ZenQ/Channel\n* INPUT_SIZE -\u003e The number of input payloads to be passed through ZenQ/Channel from producers to consumer\n\n```bash\nComputed from benchstat of 30 benchmarks each via go test -benchmem -bench=. benchmarks/simple/*.go\n\nname                                     time/op\n_Chan_NumWriters1_InputSize600-8          23.2µs ± 1%\n_ZenQ_NumWriters1_InputSize600-8          17.9µs ± 1%\n_Chan_NumWriters3_InputSize60000-8        5.27ms ± 3%\n_ZenQ_NumWriters3_InputSize60000-8        2.36ms ± 2%\n_Chan_NumWriters8_InputSize6000000-8       671ms ± 2%\n_ZenQ_NumWriters8_InputSize6000000-8       234ms ± 6%\n_Chan_NumWriters100_InputSize6000000-8     1.59s ± 4%\n_ZenQ_NumWriters100_InputSize6000000-8     309ms ± 2%\n_Chan_NumWriters1000_InputSize7000000-8    1.97s ± 0%\n_ZenQ_NumWriters1000_InputSize7000000-8    389ms ± 4%\n_Chan_Million_Blocking_Writers-8           10.4s ± 2%\n_ZenQ_Million_Blocking_Writers-8           2.32s ±21%\n\nname                                     alloc/op\n_Chan_NumWriters1_InputSize600-8           0.00B\n_ZenQ_NumWriters1_InputSize600-8           0.00B\n_Chan_NumWriters3_InputSize60000-8          109B ±68%\n_ZenQ_NumWriters3_InputSize60000-8        24.6B ±107%\n_Chan_NumWriters8_InputSize6000000-8       802B ±241%\n_ZenQ_NumWriters8_InputSize6000000-8     1.18kB ±100%\n_Chan_NumWriters100_InputSize6000000-8    44.2kB ±41%\n_ZenQ_NumWriters100_InputSize6000000-8    10.7kB ±38%\n_Chan_NumWriters1000_InputSize7000000-8    476kB ± 8%\n_ZenQ_NumWriters1000_InputSize7000000-8   90.6kB ±10%\n_Chan_Million_Blocking_Writers-8           553MB ± 0%\n_ZenQ_Million_Blocking_Writers-8           122MB ± 3%\n\nname                                     allocs/op\n_Chan_NumWriters1_InputSize600-8            0.00\n_ZenQ_NumWriters1_InputSize600-8            0.00\n_Chan_NumWriters3_InputSize60000-8          0.00\n_ZenQ_NumWriters3_InputSize60000-8          0.00\n_Chan_NumWriters8_InputSize6000000-8       2.76 ±190%\n_ZenQ_NumWriters8_InputSize6000000-8        5.47 ±83%\n_Chan_NumWriters100_InputSize6000000-8       159 ±26%\n_ZenQ_NumWriters100_InputSize6000000-8      25.1 ±39%\n_Chan_NumWriters1000_InputSize7000000-8    1.76k ± 6%\n_ZenQ_NumWriters1000_InputSize7000000-8     47.3 ±31%\n_Chan_Million_Blocking_Writers-8           2.00M ± 0%\n_ZenQ_Million_Blocking_Writers-8           1.00M ± 0%\n```\n\nThe above results show that ZenQ is more efficient than channels in all 3 metrics i.e `time/op`, `mem_alloc/op` and `num_allocs/op` for the following tested cases:-\n\n1. SPSC\n2. MPSC with NUM_WRITER_GOROUTINES \u003c NUM_CPU_CORES\n3. MPSC with NUM_WRITER_GOROUTINES \u003e NUM_CPU_CORES\n\n\n## Cherry on the Cake\n\nIn SPSC mode ZenQ is faster than channels by **92 seconds** in case of input size of 6 * 10\u003csup\u003e8\u003c/sup\u003e elements\n\n```bash\n❯ go run benchmarks/simple/main.go\n\nWith Input Batch Size: 60 and Num Concurrent Writers: 1\n\nNative Channel Runner completed transfer in: 26.916µs\nZenQ Runner completed transfer in: 20.292µs\n====================================================================\n\nWith Input Batch Size: 600 and Num Concurrent Writers: 1\n\nNative Channel Runner completed transfer in: 135.75µs\nZenQ Runner completed transfer in: 105.792µs\n====================================================================\n\nWith Input Batch Size: 6000 and Num Concurrent Writers: 1\n\nNative Channel Runner completed transfer in: 2.100209ms\nZenQ Runner completed transfer in: 510.792µs\n====================================================================\n\nWith Input Batch Size: 6000000 and Num Concurrent Writers: 1\n\nNative Channel Runner completed transfer in: 1.241481917s\nZenQ Runner completed transfer in: 226.068209ms\n====================================================================\n\nWith Input Batch Size: 600000000 and Num Concurrent Writers: 1\n\nNative Channel Runner completed transfer in: 1m55.074638875s\nZenQ Runner completed transfer in: 22.582667917s\n====================================================================\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falphadose%2FZenQ","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falphadose%2FZenQ","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falphadose%2FZenQ/lists"}