{"id":20698398,"url":"https://github.com/sozu-proxy/trie_benches","last_synced_at":"2025-04-22T21:46:15.665Z","repository":{"id":48628456,"uuid":"117566663","full_name":"sozu-proxy/trie_benches","owner":"sozu-proxy","description":"Benchmarks for sozu's trie, used in certificate selection and HTTP routing","archived":false,"fork":false,"pushed_at":"2019-03-18T14:44:19.000Z","size":113,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-29T19:11:18.245Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sozu-proxy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-01-15T16:07:54.000Z","updated_at":"2024-07-28T14:15:49.000Z","dependencies_parsed_at":"2022-08-27T10:11:49.768Z","dependency_job_id":null,"html_url":"https://github.com/sozu-proxy/trie_benches","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sozu-proxy%2Ftrie_benches","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sozu-proxy%2Ftrie_benches/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sozu-proxy%2Ftrie_benches/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sozu-proxy%2Ftrie_benches/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sozu-proxy","download_url":"https://codeload.github.com/sozu-proxy/trie_benches/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250330691,"owners_count":21413023,"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":[],"created_at":"2024-11-17T00:24:33.929Z","updated_at":"2025-04-22T21:46:15.645Z","avatar_url":"https://github.com/sozu-proxy.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sozu routing benchmarks\n\nThis project explores optimizations for the SNI certificate lookup and HTTP router\nalgorithm in the [sozu HTTP reverse proxy](https://github.com/sozu-proxy/sozu).\n\nIn sozu, configuration changes happen often at runtime, and it is designed as a\nload balancer for tens of thousands of different applications and routes.\n\nWe need a compact way to store those routes, that can be updated quickly, and\nfor which lookup is fast. As a point of comparison, a fast HTTP parser takes\n150-300ns to parse all the headers.\n\nThe current implementation in sozu is based on a trie that works on reversed\ndomain names, ie for the \"example.com\" domain, the key will be \"moc.elpmaxe\".\nIt matches on prefixes, handles the special case of wildcard domain names\nfor SNI. Once the trie has found a matching node for the domain, the node\nholds a list of URL prefixes that match to applications.\n\nThis has served sozu well in the context of Clever Cloud, where we have a lot\nof different domains, but the more common use case is to have a few domains,\nbut a lot of URL/app matches.\n\nWe also need more flexible matching patterns, like regular expressions on domain\nnames and URL.\n\n## Routing experiments\n\nthis repository holds various experiments around routing and trie optimizations,\nmostly working on domain names.\n\nSome base examples are available:\n- the \"sozu\" version uses the trie available in sozu 0.11\n- the \"hashmap\" version uses a hashmap to lookup the route (so no prefix or wildcard usage)\n- the \"linear\" version tests routes one after the other until one matches\n\n### Exp 1: trie with vector of `(key, child)`\n\nThis is a naive implementation of a trie, with wildcard domain support. It is easy\nto write, and easy to update in place.\n\n### Exp 2: trie with vector of key and vector of children\n\nBuilds on exp1 but separates the keys\n\nThis is the current version on sozu 0.11\n\n### Exp 3: keep a local prefix, and only the first byte of each child key\n\nthis implementation leverages common prefixes to only match them once.\nAnd the children will necessarily have differences on the first byte of the\nrest of their key.\n\n### Exp 4: state machines\n\nExperiment 4 uses the [fst](https://crates.io/crates/fst) crate. This comes with a\ntradeoff: since the list of keys has to be sorted, any edition requires rebuilding\nthe entire machine. We could live with this tradeoff, by making the master process\nwrite a new state machine while the workers use the previous one.\n\n### Exp 5: trie with bitvec\n\nthis one uses the [bitvec](https://crates.io/crates/bitvec) crate to test a language\nrestriction: domain name uses alphanumerical characters (we can limit to lowercase)\nand '-', '.', '*', and '_' (the underscore is not in the spec, but we've seen usage\nin the wild.\n\nWe can convert those caracters to a 6 bit encoding, and have a more compact tree,\nwith potentially faster lookup.\n\n\n### Exp 6: state machine with bitvec\n\nExperiment 6 reuses the fst from exp 4 with the bitvec language from exp 5.\n\n### Exp 7: regex set\n\nthis version makes a regex out of each route, and uses a `RegexSet` from the\n[regex](https://crates.io/crates/regex) crate to assemble them.\n\n### Exp 8: specialised trie\n\nThis is a more complex version of the exp 3 trie. It uses a cursor structure made from\nthe domain name and the URLL, and can match prefixes, regexes and SNI wildcard on it.\nAs a result, the trie nodes are a bit larger\n\n### Exp 9: tree of hashmaps\n\nThis version cuts domain names in labels, and each node contains a hashmap of the next\nlabels to the corresponding children. It uses the [hashbrown](https://crates.io/crates/hashbrown)\ncrate for its fast hashmap implementation (swisstable)\n\n### Exp 10: tree of hashmaps with SipHash\n\nThere's a case where exp 9 could be vulnerable to hash flooding, since at Clever Cloud, our users\ncontrol which domain names are used. So this one takes the code of exp 9 with the siphash\nhash algorithm (currently used as default hasher in Rust)\n\n## Benchmark results\n\ntested on a MacBook Pro (Retina, 15-inch, Late 2013), CPU 2,3 GHz Intel Core i7\n\nWe have 3 main benchmarks to compare results:\n- time to fill the structure with 100 entries, by incremental edition\n- time to lookup a known key in a structure of 1000 entries\n- time to lookup an unknown key in a structue of 1000 entries\n-\n|                           | fill structure | lookup known key  | lookup unknown key |\n| ------------------------- | -------------- | ----------------- | ------------------ |\n| linear                    |     1.35 ms    |       31 us       |     34.31 us       |\n| hashmap                   |     1.42 ms    |        7 ns       |         1 ns       |\n| current sozu impl         |     1.60 ms    |      182 ns       |       229 ns       |\n| 1: trie                   |     1.82 ms    |      138 ns       |        80 ns       |\n| 2: trie+separated keys    |     1.95 ms    |      144 ns       |        80 ns       |\n| 3: optimized trie         |     1.72 ms    |       74 ns       |        52 ns       |\n| 4: fst                    |     7.58 ms    |      354 ns       |       117 ns       |\n| 5: trie+bitvec            |    12.89 ms    |     5.79 us       |      2.08 us       |\n| 6: fst+bitvec             |    14.69 ms    |     5.82 us       |      2.12 us       |\n| 7: regexset               |    33.10 ms    |     1.15 ms       |      1.19 ms       |\n| 8 trie cursor             |     1.89 ms    |      119 ns       |        57 ns       |\n| 9: hashmap tree           |     1.73 ms    |       65 ns       |        28 ns       |\n| 10 hashmap tree + siphash |     1.89 ms    |      109 ns       |        57 ns       |\n\nWe also have a version with larger values, but eliminating the slowest versions (experiments\n4, 5, 6 and 7), as they would make a the benchmark take too long.\n- time to fill the structure with 1000 entries, by incremental edition\n- time to lookup a known key in a structure of 10000 entries\n- time to lookup an unknown key in a structue of 10000 entries\n\n|                           | fill structure | lookup known key  | lookup unknown key |\n| ------------------------- | -------------- | ----------------- | ------------------ |\n| linear                    |    12.68 ms    |   560.68 us       |       586 us       |\n| hashmap                   |    13.88 ms    |        7 ns       |         1 ns       |\n| current sozu impl         |    15.71 ms    |      230 ns       |       223 ns       |\n| 1: trie                   |    18.14 ms    |      158 ns       |        81 ns       |\n| 2: trie+separated keys    |    19.17 ms    |      159 ns       |        87 ns       |\n| 3: optimized trie         |    16.45 ms    |       83 ns       |        51 ns       |\n| 8 trie cursor             |    19.62 ms    |      127 ns       |        58 ns       |\n| 9: hashmap tree           |    16.50 ms    |       64 ns       |        28 ns       |\n| 10 hashmap tree + siphash |    18.34 ms    |      113 ns       |        58 ns       |\n\nWe can see that the trie implementations stay stable when we increase the number of\nentries.\nThey do not perform exactly the same tasks (experiment 8 especially is testing regexps\nalong with the prefixes).\nWhile version 9 and 10 looks like a quick and dirty fix, it's actually easy to write and\nmaintain, its performance is very stable, and we can make Siphash usage configurable.\nSo we will recommend adapting the tree of hashmaps as new solution.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsozu-proxy%2Ftrie_benches","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsozu-proxy%2Ftrie_benches","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsozu-proxy%2Ftrie_benches/lists"}