├── .github └── workflows │ └── test.yml ├── .gitignore ├── BENCHMARKS.md ├── LICENSE ├── Makefile ├── README.md ├── benchmarks ├── charbench.ml ├── dune ├── graphs.gp └── munge.py ├── charset.opam ├── dune-project ├── images ├── add-benchmark.svg ├── add-seq-benchmark.svg ├── cardinal-benchmark.svg ├── choose-benchmark.svg ├── choose-opt-benchmark.svg ├── compare-benchmark.svg ├── diff-benchmark.svg ├── disjoint-benchmark.svg ├── elements-benchmark.svg ├── equal-benchmark.svg ├── exists-benchmark.svg ├── fastmap-benchmark.svg ├── filter-benchmark.svg ├── filter-map-benchmark.svg ├── find-benchmark.svg ├── find-first-benchmark.svg ├── find-first-opt-benchmark.svg ├── find-last-benchmark.svg ├── find-last-opt-benchmark.svg ├── find-opt-benchmark.svg ├── fold-benchmark.svg ├── forall-benchmark.svg ├── inter-benchmark.svg ├── isempty-benchmark.svg ├── iter-benchmark.svg ├── maxelt-benchmark.svg ├── maxelt-opt-benchmark.svg ├── mem-benchmark.svg ├── minelt-benchmark.svg ├── minelt-opt-benchmark.svg ├── of-list-benchmark.svg ├── of-seq-benchmark.svg ├── partition-benchmark.svg ├── remove-benchmark.svg ├── singleton-benchmark.svg ├── slowmap-benchmark.svg ├── split-benchmark.svg ├── subset-benchmark.svg ├── to-rev-seq-benchmark.svg ├── to-seq-benchmark.svg ├── to-seq-from-benchmark.svg └── union-benchmark.svg ├── lib ├── charset.ml ├── charset.mli ├── charset_bitops.intrinsics.ml ├── charset_bitops.intrinsics.mli ├── charset_bitops.ocaml.ml ├── charset_bitops.ocaml.mli └── dune └── lib_test ├── dune ├── test.ml └── testsets.ml /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Charset 2 | on: [push, pull_request, workflow_dispatch] 3 | jobs: 4 | install: 5 | name: Install 6 | runs-on: ${{ matrix.os }} 7 | env: 8 | ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | include: 13 | - ocaml-compiler: 4.11.1 14 | os: ubuntu-latest 15 | - ocaml-compiler: 4.13.1 16 | os: ubuntu-latest 17 | - ocaml-compiler: 5.3.0 18 | os: ubuntu-latest 19 | - ocaml-compiler: 4.13.1 20 | os: macos-latest 21 | steps: 22 | 23 | - name: Checkout code 24 | uses: actions/checkout@v2 25 | 26 | - name: Use OCaml ${{ matrix.ocaml-compiler }} 27 | uses: ocaml/setup-ocaml@v3 28 | with: 29 | ocaml-compiler: ${{ matrix.ocaml-compiler }} 30 | dune-cache: true 31 | 32 | - name: Install 33 | run: opam install . --deps-only --with-test 34 | 35 | - name: Build 36 | run: opam exec -- dune build -p charset 37 | 38 | - name: Test 39 | run: opam exec -- dune exec lib_test/test.exe -- -ci true 40 | 41 | - name: Bench 42 | if: ${{ matrix.ocaml-compiler == '5.3.0' && matrix.os == 'ubuntu-latest' }} 43 | run: | 44 | opam install --yes core_bench.v0.17.0 45 | sudo apt install -y gnuplot 46 | opam exec -- make bench 47 | 48 | - name: Save benchmark results 49 | if: ${{ matrix.ocaml-compiler == '5.3.0' && matrix.os == 'ubuntu-latest' }} 50 | uses: actions/upload-artifact@v4 51 | with: 52 | name: charset-bench 53 | path: | 54 | benchmarks/csv/*.csv 55 | benchmarks/csv/*.svg 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | *~ 3 | *.native 4 | benchmarks/csv 5 | -------------------------------------------------------------------------------- /BENCHMARKS.md: -------------------------------------------------------------------------------- 1 | ## Benchmarks for [Charset](https://github.com/yallop/ocaml-charset) 2 | 3 | All pure set operations (i.e. functions on sets and elements, rather than on functions, lists, sequences, etc.) are intended to execute consistently in around 10ns or less, regardless of set size. 4 | 5 | Other operations are competitive with the corresponding standard library implementations. (The functions `find-first`, `find-first-opt`, `find-last` and `find-last-opt` are currently an exception; they are significantly slower than the standard library implementations.) 6 | 7 | #### pure set operations (should be <~10ns) 8 | 9 | ```ocaml 10 | val is_empty : t -> bool 11 | ``` 12 | ![isempty][isempty] 13 | 14 | ```ocaml 15 | val mem : elt -> t -> bool 16 | ``` 17 | ![mem][mem] 18 | 19 | ```ocaml 20 | val add : elt -> t -> t val remove : elt -> t -> t 21 | ``` 22 | ![add][add] ![remove][remove] 23 | 24 | ```ocaml 25 | val singleton : elt -> t 26 | ``` 27 | ![singleton][singleton] 28 | 29 | 30 | ```ocaml 31 | val union : t -> t -> t val inter : t -> t -> t 32 | ``` 33 | ![union][union] ![inter][inter] 34 | 35 | ```ocaml 36 | val disjoint : t -> t -> bool 37 | ``` 38 | ![disjoint][disjoint] 39 | 40 | 41 | ```ocaml 42 | val diff : t -> t -> t 43 | ``` 44 | ![diff][diff] 45 | 46 | 47 | ```ocaml 48 | val compare : t -> t -> int val equal : t -> t -> bool 49 | ``` 50 | ![compare][compare] ![equal][equal] 51 | 52 | ```ocaml 53 | val subset : t -> t -> bool 54 | ``` 55 | ![subset][subset] 56 | 57 | ```ocaml 58 | val cardinal : t -> int 59 | ``` 60 | ![cardinal][cardinal] 61 | 62 | 63 | ```ocaml 64 | val min_elt : t -> elt val min_elt_opt : t -> elt option 65 | ``` 66 | ![minelt][minelt] ![minelt-opt][minelt-opt] 67 | 68 | ```ocaml 69 | val max_elt : t -> elt val max_elt_opt : t -> elt option 70 | ``` 71 | ![maxelt][maxelt] ![maxelt-opt][maxelt-opt] 72 | 73 | ```ocaml 74 | val choose : t -> elt val choose_opt : t -> elt option 75 | ``` 76 | ![choose][choose] ![choose-opt][choose-opt] 77 | 78 | ```ocaml 79 | val find : elt -> t -> elt val find_opt : elt -> t -> elt option 80 | ``` 81 | ![find][find] ![find-opt][find-opt] 82 | 83 | ```ocaml 84 | val split : elt -> t -> t * bool * t 85 | ``` 86 | ![split][split] 87 | 88 | #### other operations, which use functions, sequences, lists, etc. (should be competitive with the standard library) 89 | 90 | ```ocaml 91 | val iter : (elt -> unit) -> t -> unit 92 | ``` 93 | ![iter][iter] 94 | 95 | 96 | ```ocaml 97 | val map : (elt -> elt) -> t -> t 98 | ``` 99 | ![fastmap][fastmap] ![slowmap][slowmap] 100 | 101 | ```ocaml 102 | val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a 103 | ``` 104 | ![fold][fold] 105 | 106 | ```ocaml 107 | val exists : (elt -> bool) -> t -> bool val for_all : (elt -> bool) -> t -> bool 108 | ``` 109 | ![exists][exists] ![forall][forall] 110 | 111 | 112 | ```ocaml 113 | val filter : (elt -> bool) -> t -> t val filter_map : (elt -> elt option) -> t -> t 114 | ``` 115 | ![filter][filter] ![filter-map][filter-map] 116 | 117 | 118 | ```ocaml 119 | val partition : (elt -> bool) -> t -> t * t 120 | ``` 121 | ![partition][partition] 122 | 123 | ```ocaml 124 | val elements : t -> elt list 125 | ``` 126 | ![elements][elements] 127 | 128 | 129 | ```ocaml 130 | val of_list : elt list -> t 131 | ``` 132 | ![of-list][of-list] 133 | 134 | ```ocaml 135 | val to_seq_from : elt -> t -> elt Seq.t 136 | ``` 137 | ![to-seq-from][to-seq-from] 138 | 139 | ```ocaml 140 | val to_seq : t -> elt Seq.t 141 | ``` 142 | ![to-seq][to-seq] 143 | 144 | ```ocaml 145 | val to_rev_seq : t -> elt Seq.t 146 | ``` 147 | ![to-rev-seq][to-rev-seq] 148 | 149 | ```ocaml 150 | val add_seq : elt Seq.t -> t -> t 151 | ``` 152 | ![add-seq][add-seq] 153 | 154 | 155 | ```ocaml 156 | val of_seq : elt Seq.t -> t 157 | ``` 158 | ![of-seq][of-seq] 159 | 160 | #### operations that currently have inefficient implementations 161 | 162 | ```ocaml 163 | val find_first : (elt -> bool) -> t -> elt val find_first_opt : (elt -> bool) -> t -> elt option 164 | ``` 165 | ![find-first][find-first] ![find-first-opt][find-first-opt] 166 | 167 | ```ocaml 168 | val find_last : (elt -> bool) -> t -> elt val find_last_opt : (elt -> bool) -> t -> elt option 169 | ``` 170 | ![find-last][find-last] ![find-last-opt][find-last-opt] 171 | 172 | 173 | -------------------------------------------------------------------------------- 174 | 175 | [cardinal]: images/cardinal-benchmark.svg 176 | [disjoint]: images/disjoint-benchmark.svg 177 | [exists]: images/exists-benchmark.svg 178 | [forall]: images/forall-benchmark.svg 179 | [minelt]: images/minelt-benchmark.svg 180 | [maxelt]: images/maxelt-benchmark.svg 181 | [union]: images/union-benchmark.svg 182 | [compare]: images/compare-benchmark.svg 183 | [elements]: images/elements-benchmark.svg 184 | [fastmap]: images/fastmap-benchmark.svg 185 | [inter]: images/inter-benchmark.svg 186 | [slowmap]: images/slowmap-benchmark.svg 187 | [diff]: images/diff-benchmark.svg 188 | [equal]: images/equal-benchmark.svg 189 | [filter]: images/filter-benchmark.svg 190 | [isempty]: images/isempty-benchmark.svg 191 | [subset]: images/subset-benchmark.svg 192 | [add]: images/add-benchmark.svg 193 | [find]: images/find-benchmark.svg 194 | [mem]: images/mem-benchmark.svg 195 | [remove]: images/remove-benchmark.svg 196 | [find-opt]: images/find-opt-benchmark.svg 197 | [maxelt-opt]: images/maxelt-opt-benchmark.svg 198 | [minelt-opt]: images/minelt-opt-benchmark.svg 199 | [choose]: images/choose-benchmark.svg 200 | [choose-opt]: images/choose-opt-benchmark.svg 201 | [singleton]: images/singleton-benchmark.svg 202 | [split]: images/split-benchmark.svg 203 | [iter]: images/iter-benchmark.svg 204 | [add-seq]: images/add-seq-benchmark.svg 205 | [filter-map]: images/filter-map-benchmark.svg 206 | [of-seq]: images/of-seq-benchmark.svg 207 | [to-rev-seq]: images/to-rev-seq-benchmark.svg 208 | [to-seq]: images/to-seq-benchmark.svg 209 | [to-seq-from]: images/to-seq-from-benchmark.svg 210 | [find-first-opt]: images/find-first-opt-benchmark.svg 211 | [find-last-opt]: images/find-last-opt-benchmark.svg 212 | [find-first]: images/find-first-benchmark.svg 213 | [find-last]: images/find-last-benchmark.svg 214 | [fold]: images/fold-benchmark.svg 215 | [of-list]: images/of-list-benchmark.svg 216 | [partition]: images/partition-benchmark.svg 217 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Jeremy Yallop 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BENCHMARKS=isempty \ 2 | mem \ 3 | add \ 4 | singleton \ 5 | remove \ 6 | union \ 7 | inter \ 8 | disjoint \ 9 | diff \ 10 | compare \ 11 | equal \ 12 | subset \ 13 | iter \ 14 | fastmap \ 15 | slowmap \ 16 | fold \ 17 | forall \ 18 | exists \ 19 | filter \ 20 | filter-map \ 21 | partition \ 22 | cardinal \ 23 | elements \ 24 | minelt \ 25 | minelt-opt \ 26 | maxelt \ 27 | maxelt-opt \ 28 | choose \ 29 | choose-opt \ 30 | split \ 31 | find \ 32 | find-opt \ 33 | find-first \ 34 | find-first-opt \ 35 | find-last \ 36 | find-last-opt \ 37 | of-list \ 38 | to-seq-from \ 39 | to-seq \ 40 | to-rev-seq \ 41 | add-seq \ 42 | of-seq 43 | 44 | BENCHARGS=-quota 0.2 -display blank -clear-columns time 45 | 46 | all: 47 | dune build -p charset 48 | 49 | clean: 50 | dune clean 51 | 52 | test: 53 | dune runtest 54 | 55 | bench: $(foreach b,$(BENCHMARKS),bench-$(b)) 56 | 57 | bench-%: 58 | mkdir -p benchmarks/csv 59 | dune exec --profile benchmark -- charset_bench $* $(BENCHARGS) \ 60 | | benchmarks/munge.py > benchmarks/csv/$*-benchmark.csv 61 | gnuplot -c benchmarks/graphs.gp $* benchmarks/csv/$*-benchmark.csv benchmarks/csv/$*-benchmark.svg 62 | 63 | 64 | update-images: 65 | for bench in $(BENCHMARKS); do cp benchmarks/csv/$${bench}-benchmark.svg images/; done 66 | 67 | .PHONY: all clean test bench update-images 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Charset 2 | 3 | [![Charset](https://github.com/yallop/ocaml-charset/actions/workflows/test.yml/badge.svg)](https://github.com/yallop/ocaml-charset/actions/workflows/test.yml) 4 | 5 | This library provides a fast drop-in replacement for the standard library's `Set.Make(Char)`, implemented as a bit array. All functions follow the documented behaviour of the corresponding standard library functions. 6 | 7 | All pure set operations (i.e. functions on sets and elements, rather than on functions, lists, sequences, etc.) are intended to execute consistently in around 10ns or less, regardless of set size: 8 | 9 | ```ocaml 10 | val union : t -> t -> t 11 | ``` 12 | ![union][union] 13 | 14 | 15 | ```ocaml 16 | val equal : t -> t -> bool 17 | ``` 18 | ![equal][equal] 19 | 20 | Other operations are competitive with the corresponding standard library implementations: 21 | 22 | ```ocaml 23 | val of_list : elt list -> t 24 | ``` 25 | ![of-list][of-list] 26 | 27 | ```ocaml 28 | val iter : (elt -> unit) -> t -> unit 29 | ``` 30 | ![iter][iter] 31 | 32 | See [BENCHMARKS.md](https://github.com/yallop/ocaml-charset/blob/master/BENCHMARKS.md) for a full set of benchmark results. 33 | 34 | -------------------------------------------------------------------------------- 35 | 36 | [union]: images/union-benchmark.svg 37 | [iter]: images/iter-benchmark.svg 38 | [equal]: images/equal-benchmark.svg 39 | [of-list]: images/of-list-benchmark.svg 40 | 41 | -------------------------------------------------------------------------------- /benchmarks/charbench.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) 2022 Jeremy Yallop. 3 | * 4 | * This file is distributed under the terms of the MIT License. 5 | * See the file LICENSE for details. 6 | *) 7 | 8 | let sizes = [0;1;2;4;8;16;32;64;96;128;160;192;224;256] 9 | 10 | module type S = module type of Charset 11 | 12 | module Common(X: S) = 13 | struct 14 | let all = X.of_list (List.init 256 Stdlib.Char.chr) 15 | 16 | let sets n = 17 | let set1, set2 = X.partition (fun _ -> Random.int 256 < n) all in 18 | let set3, set4 = X.partition (fun _ -> Random.int 256 < n) all in 19 | set1, set2, set3, set4 20 | 21 | let min_elt n = 22 | let (set1,_,_,_) = sets n in 23 | Core.Staged.stage @@ fun () -> 24 | match X.min_elt set1 with 25 | | exception Not_found -> () 26 | | _ -> () 27 | 28 | let min_elt_opt n = 29 | let (set1,_,_,_) = sets n in 30 | Core.Staged.stage @@ fun () -> 31 | ignore (X.min_elt_opt set1) 32 | 33 | let choose n = 34 | let (set1,_,_,_) = sets n in 35 | Core.Staged.stage @@ fun () -> 36 | match X.choose set1 with 37 | | exception Not_found -> () 38 | | _ -> () 39 | 40 | let choose_opt n = 41 | let (set1,_,_,_) = sets n in 42 | Core.Staged.stage @@ fun () -> 43 | ignore (X.choose_opt set1) 44 | 45 | let max_elt n = 46 | let (set1,_,_,_) = sets n in 47 | Core.Staged.stage @@ fun () -> 48 | match X.max_elt set1 with 49 | | exception Not_found -> () 50 | | _ -> () 51 | 52 | let max_elt_opt n = 53 | let (set1,_,_,_) = sets n in 54 | Core.Staged.stage @@ fun () -> 55 | ignore (X.max_elt_opt set1) 56 | 57 | let union n = 58 | let (set1,_,set3,_) = sets n in 59 | Core.Staged.stage @@ fun () -> 60 | ignore (X.union set1 set3) 61 | 62 | let inter n = 63 | let (set1,_,set3,_) = sets n in 64 | Core.Staged.stage @@ fun () -> 65 | ignore (X.inter set1 set3) 66 | 67 | let diff n = 68 | let (set1,_,set3,_) = sets n in 69 | Core.Staged.stage @@ fun () -> 70 | ignore (X.diff set1 set3) 71 | 72 | let compare n = 73 | let (set1,_,set3,_) = sets n in 74 | Core.Staged.stage @@ fun () -> 75 | ignore (X.compare set1 set3) 76 | 77 | let equal n = 78 | let (set1,_,set3,_) = sets n in 79 | Core.Staged.stage @@ fun () -> 80 | ignore (X.equal set1 set3) 81 | 82 | let cardinal n = 83 | let (set1,_,_,_) = sets n in 84 | Core.Staged.stage @@ fun () -> 85 | ignore (X.cardinal set1) 86 | 87 | let subset n = 88 | let (set1,_,set3,_) = sets n in 89 | Core.Staged.stage @@ fun () -> 90 | ignore (X.subset set1 set3) 91 | 92 | let disjoint n = 93 | let (set1,_,set3,_) = sets n in 94 | Core.Staged.stage @@ fun () -> 95 | ignore (X.disjoint set1 set3) 96 | 97 | let for_all n = 98 | let (set1,_,_,_) = sets n in 99 | Core.Staged.stage @@ fun () -> 100 | ignore (X.for_all (fun _ -> true) set1) 101 | 102 | let exists n = 103 | let (set1,_,_,_) = sets n in 104 | Core.Staged.stage @@ fun () -> 105 | ignore (X.exists (fun _ -> false) set1) 106 | 107 | let filter n = 108 | let (set1,_,_,_) = sets n in 109 | Core.Staged.stage @@ fun () -> 110 | ignore (X.filter (fun c -> Char.lowercase_ascii c <> c) set1) 111 | 112 | let filter_map n = 113 | let (set1,_,_,_) = sets n in 114 | Core.Staged.stage @@ fun () -> 115 | ignore (X.filter_map Option.some set1) 116 | 117 | let elements n = 118 | let (set1,_,_,_) = sets n in 119 | Core.Staged.stage @@ fun () -> 120 | ignore (X.elements set1) 121 | 122 | let of_list n = 123 | let (set1,_,_,_) = sets n in 124 | let elements = X.elements set1 in 125 | Core.Staged.stage @@ fun () -> 126 | ignore (X.of_list elements) 127 | 128 | let fastmap n = 129 | let (set1,_,_,_) = sets n in 130 | Core.Staged.stage @@ fun () -> 131 | ignore (X.map Fun.id set1) 132 | 133 | let slowmap n = 134 | let (set1,_,_,_) = sets n in 135 | Core.Staged.stage @@ fun () -> 136 | ignore (X.map (fun c -> Char.unsafe_chr (256 - Char.code c)) set1) 137 | 138 | let fold n = 139 | let (set1,_,_,_) = sets n in 140 | Core.Staged.stage @@ fun () -> 141 | ignore (X.fold List.cons set1 []) 142 | 143 | let iter n = 144 | let (set1,_,_,_) = sets n in 145 | Core.Staged.stage @@ fun () -> 146 | ignore (X.iter ignore set1) 147 | 148 | let is_empty n = 149 | let (set1,_,_,_) = sets n in 150 | Core.Staged.stage @@ fun () -> 151 | ignore (X.is_empty set1) 152 | 153 | let mem n = 154 | let (set,_,_,_) = sets n in 155 | Core.Staged.stage @@ fun () -> 156 | ignore (X.mem 'a' set) 157 | 158 | let add n = 159 | let (set,_,_,_) = sets n in 160 | Core.Staged.stage @@ fun () -> 161 | ignore (X.add 'a' set) 162 | 163 | let remove n = 164 | let (set,_,_,_) = sets n in 165 | Core.Staged.stage @@ fun () -> 166 | ignore (X.remove 'a' set) 167 | 168 | let find n = 169 | let (set,_,_,_) = sets n in 170 | Core.Staged.stage @@ fun () -> 171 | match X.find 'a' set with 172 | | exception Not_found -> () 173 | | _ -> () 174 | 175 | let find_opt n = 176 | let (set,_,_,_) = sets n in 177 | Core.Staged.stage @@ fun () -> 178 | ignore (X.find_opt 'a' set) 179 | 180 | let singleton _ = (* TODO: this shouldn't be parameterized *) 181 | Core.Staged.stage @@ fun () -> 182 | ignore (X.singleton 'a') 183 | 184 | let split n = 185 | let (set,_,_,_) = sets n in 186 | Core.Staged.stage @@ fun () -> 187 | ignore (X.split 'a' set) 188 | 189 | let of_seq n = 190 | let (set,_,_,_) = sets n in 191 | let seq = List.to_seq (X.elements set) in 192 | Core.Staged.stage @@ fun () -> 193 | ignore (X.of_seq seq) 194 | 195 | let add_seq n = 196 | let (set1,_,set3,_) = sets n in 197 | let seq = List.to_seq (X.elements set1) in 198 | Core.Staged.stage @@ fun () -> 199 | ignore (X.add_seq seq set3) 200 | 201 | let to_seq n = 202 | let (set,_,_,_) = sets n in 203 | Core.Staged.stage @@ fun () -> 204 | Seq.iter ignore (X.to_seq set) 205 | 206 | let to_rev_seq n = 207 | let (set,_,_,_) = sets n in 208 | Core.Staged.stage @@ fun () -> 209 | Seq.iter ignore (X.to_rev_seq set) 210 | 211 | let to_seq_from n = 212 | let (set,_,_,_) = sets n in 213 | Core.Staged.stage @@ fun () -> 214 | Seq.iter ignore (X.to_seq_from 'a' set) 215 | 216 | let find_first n = 217 | let (set,_,_,_) = sets n in 218 | Core.Staged.stage @@ fun () -> 219 | match X.find_first ((<=)'\127') set with 220 | | exception Not_found -> () 221 | | _ -> () 222 | 223 | let find_first_opt n = 224 | let (set,_,_,_) = sets n in 225 | Core.Staged.stage @@ fun () -> 226 | ignore (X.find_first_opt ((<=)'\127') set) 227 | 228 | let find_last n = 229 | let (set,_,_,_) = sets n in 230 | Core.Staged.stage @@ fun () -> 231 | match X.find_last ((>=)'\127') set with 232 | | exception Not_found -> () 233 | | _ -> () 234 | 235 | let find_last_opt n = 236 | let (set,_,_,_) = sets n in 237 | Core.Staged.stage @@ fun () -> 238 | ignore (X.find_last_opt ((>=)'\127') set) 239 | 240 | let partition n = 241 | let (set,_,_,_) = sets n in 242 | Core.Staged.stage @@ fun () -> 243 | ignore (X.partition ((>=)'\127') set) 244 | end 245 | 246 | module Stdlib_CharSet = 247 | struct 248 | module C = Stdlib.Set.Make(Char) 249 | (* inefficient version for pre-4.12 compatibility *) 250 | let[@ocaml.warning "-32"] to_rev_seq s = List.to_seq (List.rev (C.elements s)) 251 | let[@ocaml.warning "-32"] to_list = C.elements 252 | include C 253 | end 254 | module Stdtests = Common(Stdlib_CharSet) 255 | module Ourtests = Common(Charset) 256 | 257 | open Core_bench 258 | 259 | let group name f g = 260 | (name, 261 | Bench.make_command [ 262 | Bench.Test.create_indexed ~name:("stdlib_"^ name) ~args:sizes f; 263 | Bench.Test.create_indexed ~name:("charset_"^ name) ~args:sizes g; 264 | ]) 265 | 266 | let () = 267 | Command_unix.run 268 | (Core.Command.group ~summary:"char benchmarks" 269 | [group "isempty" Stdtests.is_empty Ourtests.is_empty; 270 | group "mem" Stdtests.mem Ourtests.mem; 271 | group "add" Stdtests.add Ourtests.add; 272 | group "singleton" Stdtests.singleton Ourtests.singleton; 273 | group "remove" Stdtests.remove Ourtests.remove; 274 | group "union" Stdtests.union Ourtests.union; 275 | group "inter" Stdtests.inter Ourtests.inter; 276 | group "disjoint" Stdtests.disjoint Ourtests.disjoint; 277 | group "diff" Stdtests.diff Ourtests.diff; 278 | group "compare" Stdtests.compare Ourtests.compare; 279 | group "equal" Stdtests.equal Ourtests.equal; 280 | group "subset" Stdtests.subset Ourtests.subset; 281 | group "iter" Stdtests.iter Ourtests.iter; 282 | group "fastmap" Stdtests.fastmap Ourtests.fastmap; 283 | group "slowmap" Stdtests.slowmap Ourtests.slowmap; 284 | group "fold" Stdtests.fold Ourtests.fold; 285 | group "forall" Stdtests.for_all Ourtests.for_all; 286 | group "exists" Stdtests.exists Ourtests.exists; 287 | group "filter" Stdtests.filter Ourtests.filter; 288 | group "filter-map" Stdtests.filter_map Ourtests.filter_map; 289 | group "partition" Stdtests.partition Ourtests.partition; 290 | group "cardinal" Stdtests.cardinal Ourtests.cardinal; 291 | group "elements" Stdtests.elements Ourtests.elements; 292 | group "minelt" Stdtests.min_elt Ourtests.min_elt; 293 | group "minelt-opt" Stdtests.min_elt_opt Ourtests.min_elt_opt; 294 | group "maxelt" Stdtests.max_elt Ourtests.max_elt; 295 | group "maxelt-opt" Stdtests.max_elt_opt Ourtests.max_elt_opt; 296 | group "choose" Stdtests.choose Ourtests.choose; 297 | group "choose-opt" Stdtests.choose_opt Ourtests.choose_opt; 298 | group "split" Stdtests.split Ourtests.split; 299 | group "find" Stdtests.find Ourtests.find; 300 | group "find-opt" Stdtests.find_opt Ourtests.find_opt; 301 | group "find-first" Stdtests.find_first Ourtests.find_first; 302 | group "find-first-opt" Stdtests.find_first_opt Ourtests.find_first_opt; 303 | group "find-last" Stdtests.find_last Ourtests.find_last; 304 | group "find-last-opt" Stdtests.find_last_opt Ourtests.find_last_opt; 305 | group "of-list" Stdtests.of_list Ourtests.of_list; 306 | group "to-seq-from" Stdtests.to_seq_from Ourtests.to_seq_from; 307 | group "to-seq" Stdtests.to_seq Ourtests.to_seq; 308 | group "to-rev-seq" Stdtests.to_rev_seq Ourtests.to_rev_seq; 309 | group "add-seq" Stdtests.add_seq Ourtests.add_seq; 310 | group "of-seq" Stdtests.of_seq Ourtests.of_seq; 311 | ]) 312 | -------------------------------------------------------------------------------- /benchmarks/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (public_name charset_bench) 3 | (name charbench) 4 | (libraries charset core_bench) 5 | (enabled_if (= %{profile} benchmark))) 6 | -------------------------------------------------------------------------------- /benchmarks/graphs.gp: -------------------------------------------------------------------------------- 1 | set datafile separator "," 2 | set term svg enhanced background rgb 'white' size 300,200 font "Verdana,9" 3 | set xlabel "set size" 4 | set ylabel "ns" 5 | set logscale y 6 | set key autotitle columnhead 7 | 8 | set style line 1 lc rgb '#8b1a0e' pt 1 ps 1 lt 1 lw 3 # --- red 9 | set style line 2 lc rgb '#5e9c36' pt 6 ps 1 lt 1 lw 3 # --- green 10 | 11 | set xrange [0:256] 12 | set yrange [0:] 13 | 14 | set title ARG1 15 | set key bottom right 16 | 17 | set output ARG3 18 | plot ARG2 using 1:2 with linespoints ls 102, ARG2 using 1:3 with linespoints ls 103 19 | -------------------------------------------------------------------------------- /benchmarks/munge.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import re, sys, pprint 4 | 5 | matcher = re.compile( 6 | '^ *(?P.*)' 7 | '_' 8 | '(?P[^_]*)' 9 | ':' 10 | '(?P[0-9]+)' 11 | ' +' 12 | '(?P