{"id":13726093,"url":"https://github.com/dinosaure/art","last_synced_at":"2025-03-15T13:30:57.413Z","repository":{"id":40577231,"uuid":"222748784","full_name":"dinosaure/art","owner":"dinosaure","description":"Adaptive Radix Tree in OCaml","archived":false,"fork":false,"pushed_at":"2024-08-06T08:01:32.000Z","size":710,"stargazers_count":48,"open_issues_count":8,"forks_count":1,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-02-27T01:53:00.623Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"OCaml","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/dinosaure.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE.md","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":"2019-11-19T17:11:40.000Z","updated_at":"2025-01-17T19:05:53.000Z","dependencies_parsed_at":"2024-11-02T19:16:46.502Z","dependency_job_id":null,"html_url":"https://github.com/dinosaure/art","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dinosaure%2Fart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dinosaure%2Fart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dinosaure%2Fart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dinosaure%2Fart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dinosaure","download_url":"https://codeload.github.com/dinosaure/art/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243735799,"owners_count":20339531,"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-08-03T01:02:52.351Z","updated_at":"2025-03-15T13:30:56.909Z","avatar_url":"https://github.com/dinosaure.png","language":"OCaml","funding_links":[],"categories":["OCaml"],"sub_categories":[],"readme":"## Adaptive Radix Tree (ART) in OCaml\n\nThis is an implementation in OCaml of [ART][ART]. Adaptive Radix Tree is like a\nsimple `Hashtbl` with order:\n\n```ocaml\n# let tree = Art.make () ;;\n# Art.insert tree (Art.key \"foo\") 42 ;;\n# Art.insert tree (Art.key \"bar\") 21 ;;\n# Art.find tree (Art.key \"foo\")\n- : int = 42\n```\n\nOperation like `minimum` or `maximum` are available (which don't exist for a\nsimple `Hashtbl.t`):\n\n```ocaml\n# let tree = Art.make () ;;\n# Art.insert tree (Art.key \"0\") 0 ;;\n# Art.insert tree (Art.key \"1\") 1 ;;\n# Art.insert tree (Art.key \"2\") 2 ;;\n# Art.minimum tree\n- : int = 0\n# Art.maximum tree\n- : int = 2\n```\n\nIf you want the order and the speed of `Hashtbl.t`, Art is your library:\n- Benchmark on [`find`][find-bechamel]\n- Benchmark on [`insert`][insert-bechamel]\n\nThe function `prefix_iter` is also available if you want to get a subset of your\ntree:\n```ocaml\n# let t = Art.make () ;;\n# Art.insert t (Art.key\n# Art.insert t (Art.key \"Dalton Joe\") 0 ;;\n# Art.insert t (Art.key \"Dalton Jack\") 1 ;;\n# Art.insert t (Art.key \"Dalton William\") 2 ;;\n# Art.insert t (Art.key \"Dalton Averell\") 3 ;;\n# Art.insert t (Art.key \"Rantanplan\") 4 ;;\n# let dalton = Art.prefix_iter ~prefix:(Art.key \"Dalton\")\n  (fun k _ a -\u003e (k :\u003e string) :: a) [] t ;;\n- : string list = [ \"Dalton Joe\"\n                  ; \"Dalton Jack\"\n\t\t  ; \"Dalton William\"\n\t\t  ; \"Dalton Averell\" ]\n```\n\n\n## Read Optimised Write Exclusion (ROWEX) in OCaml\n\nROWEX is a second implementation of ART with atomic operations. It's a _functor_\nwhich expects an implementation of atomic operations such as `load` or `store`.\n\n### Parallelism, atomic operation \u0026 OCaml\n\nThe current version of OCaml has a global lock for the GC. By this way, it's not\npossible for us to execute ROWEX operations (`find`/`insert`) with true\nparallelism if we use the same OCaml runtime. Even if you use LWT or ASYNC, you\nexecute jobs concurrently.\n\nHowever, ROWEX wants to provide an implementation where `find`/`insert` can be\nexecuted in parallel without any problems (race condition or ABA problem). So\nROWEX provides an implementation, `persistent`, which implements atomic\noperations on a memory area. Then, we are able, as [`parmap`][parmap], to\nsimulate true parallelism as long as each operations are executed into their own\n[`fork()`][fork].\n\nThe goal of this library is provide:\n- the most easy way to switch the implementation to\n  [ocaml-multicore][ocaml-multicore]\n- a baby step to be able to manipulate a file by several processes\n  (consumers/`find`, producers/`insert`) in parallel\n\nROWEX follows two main papers:\n- The initial implementation of [ROWEX][ROWEX]\n- A derivation of it to be **persistent**: [PART][PART]\n\n### Tools\n\nThe distribution comes with some tools to manipulate an _index_:\n```sh\n$ opam pin add -y https://github.com/dinosaure/art\n$ opam install rowex\n$ part.make index.idx\n$ ls -lh\n-rw-r--r-- 1 user user 8,0M ----- -- --:-- index.idx\nprw------- 1 user user    0 ----- -- --:-- index.idx.socket\nprw------- 1 user user    0 ----- -- --:-- index.idx-truncate.socket\n$ part.insert index.idx foo 1\n$ part.find index.idx foo\n1\n```\n\nOn the OCaml side, a `Part` module exists which implements these functions:\n```ocaml\ntype 'a t constraint 'a = [\u003c `Rd | `Wr ]\n\nval create : ?len:int -\u003e string -\u003e unit\nval insert : [\u003e `Rd | `Wr ] t -\u003e string -\u003e int -\u003e unit\nval lookup : [\u003e `Rd ] t -\u003e string -\u003e int\n```\n\n`part` is Unix dependent (and it need an **Unix named pipe**). It ensures with\nexplained internal mechanisms to use multiple readers and one writer:\n- The _writer_ can take the exclusive ownership on the index file and its named\n  pipe\n- _readers_ don't need to take the ownership but they must send a signal into\n  the named pipe (to the _writer_) that they start to introspect the index\n\nFor _readers_, some functions exist to signal their existence to the _write_:\n```ocaml\nval append_reader : Ipc.t -\u003e unit\nval delete_reader : Ipc.t -\u003e unit\n\nval ipc : _ t -\u003e Ipc.t\n```\n \n### Status: experimental\n\nThis part of the distribution is **experimental** - even if the distribution\ncomes with several tests to ensure that the implementation works, ROWEX is\nfragile! It still need a synchronization mechanism `fsync()` which is added\npervasively in some parts of the code according to outcomes of errors.\n\n[ART]: https://db.in.tum.de/~leis/papers/ART.pdf\n[ROWEX]: https://db.in.tum.de/~leis/papers/artsync.pdf\n[PART]: https://arxiv.org/pdf/1909.13670.pdf\n[find-bechamel]: https://dinosaure.github.io/art/bench/find.html\n[insert-bechamel]: https://dinosaure.github.io/art/bench/insert.html\n[parmap]: https://github.com/rdicosmo/parmap\n[fork]: https://man7.org/linux/man-pages/man2/fork.2.html\n[ocaml-multicore]: https://github.com/ocaml-multicore/ocaml-multicore\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdinosaure%2Fart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdinosaure%2Fart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdinosaure%2Fart/lists"}