├── .github └── workflows │ └── go.yml ├── LICENSE.md ├── README.md ├── benchmark_test.go ├── benchmarks ├── README.md ├── roar32.txt ├── roar64-serial.txt ├── roar64.txt └── sroar.txt ├── bitmap.go ├── bitmap_test.go ├── container.go ├── go.mod ├── go.sum ├── iterator.go ├── iterator_test.go ├── keys.go ├── real_data_test.go ├── setutil.go └── utils.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.17 20 | 21 | - name: Test 22 | run: go test -v ./... 23 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | --- 179 | 180 | Portion of the code is copied over from [Roaring Bitmaps][roar], under Apache 2.0 license. 181 | 182 | [roar]: https://github.com/RoaringBitmap/roaring/blob/master/LICENSE 183 | 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sroar: Serialized Roaring Bitmaps 2 | 3 | sroar is a re-written version of Roaring Bitmaps in Go, with the aim to have 4 | equality between in-memory representation and on-disk representation. An 5 | sroar.Bitmap does not need to be marshalled or unmarshalled, as the underlying 6 | represetation is a byte slice. Therefore, it can be written to disk, brought to 7 | memory, or shipped over the network immediately. This is needed in [Dgraph][], where 8 | we need to deal with lots of bitmaps. 9 | 10 | sroar only implements array and bitmap containers. It does NOT implement run 11 | containers, which is an optimization that RoaringBitmaps has. Despite that, it 12 | outperforms RoaringBitmaps as shown in the Benchmarks section. 13 | 14 | [Dgraph]: https://github.com/dgraph-io/dgraph 15 | [Roaring]: https://github.com/RoaringBitmap/roaring 16 | 17 | The code borrows concepts and code from [RoaringBitmaps][Roaring]. 18 | 19 | ## Benchmarks 20 | 21 | The benchmarks were run: 22 | - Using real data set as described in [RoaringBitmaps][Roaring]. 23 | - Only on the 64-bit version of roaring bitmaps (roaring64). 24 | - Only on `FastOr`, which is the more expensive operation than `And` or 25 | equivalent. 26 | - On AMD Ryzen Threadripper 2950X 16-Core Processor. 27 | - Using Go benchmarks serially. 28 | 29 | Based on the benchmarks, sroar is: 30 | - 6.5x faster (-85% p50) for benchmarks >1ms, uses 31 | - 15x (-93.5% p50) less memory for allocations >1MB. 32 | - 25x fewer allocations. 33 | 34 | The benchmark command and the results are: 35 | 36 | ``` 37 | $ go test -bench BenchmarkRealDataFastOr --run=XXX --count=5 --benchmem 38 | 39 | name CPU old time/op new time/op delta 40 | RealDataFastOr/census1881-32 302ms ± 2% 2ms ± 3% -99.29% (p=0.008 n=5+5) 41 | RealDataFastOr/wikileaks-noquotes-32 76.5ms ± 1% 0.9ms ± 1% -98.83% (p=0.008 n=5+5) 42 | RealDataFastOr/wikileaks-noquotes_srt-32 34.8ms ± 5% 1.0ms ± 2% -97.07% (p=0.008 n=5+5) 43 | RealDataFastOr/dimension_033-32 55.0ms ± 3% 2.7ms ± 0% -95.16% (p=0.016 n=5+4) 44 | RealDataFastOr/census1881_srt-32 36.8ms ± 3% 2.9ms ± 1% -92.13% (p=0.008 n=5+5) 45 | RealDataFastOr/dimension_003-32 50.4ms ± 1% 11.6ms ± 4% -77.06% (p=0.008 n=5+5) 46 | RealDataFastOr/dimension_008-32 10.0ms ± 2% 3.7ms ± 2% -62.69% (p=0.008 n=5+5) 47 | RealDataFastOr/weather_sept_85_srt-32 6.13ms ± 3% 2.72ms ± 2% -55.66% (p=0.008 n=5+5) 48 | RealDataFastOr/census-income-32 1.70ms ± 3% 1.05ms ± 1% -38.53% (p=0.008 n=5+5) 49 | RealDataFastOr/weather_sept_85-32 2.28ms ± 2% 4.07ms ± 2% +78.52% (p=0.008 n=5+5) 50 | 51 | RealDataFastOr/uscensus2000-32 556µs ± 2% 791µs ± 1% +42.17% (p=0.008 n=5+5) 52 | RealDataFastOr/census-income_srt-32 260µs ± 4% 986µs ± 2% +279.09% (p=0.008 n=5+5) 53 | 54 | name MEM_BYTES old alloc/op new alloc/op delta 55 | RealDataFastOr/census1881-32 585MB ± 0% 1MB ± 0% -99.75% (p=0.008 n=5+5) 56 | RealDataFastOr/wikileaks-noquotes-32 76.3MB ± 0% 0.6MB ± 0% -99.24% (p=0.008 n=5+5) 57 | RealDataFastOr/wikileaks-noquotes_srt-32 22.8MB ± 0% 0.6MB ± 0% -97.46% (p=0.008 n=5+5) 58 | RealDataFastOr/census1881_srt-32 15.3MB ± 0% 1.4MB ± 0% -90.58% (p=0.008 n=5+5) 59 | RealDataFastOr/dimension_003-32 7.78MB ± 0% 1.44MB ± 0% -81.49% (p=0.008 n=5+5) 60 | RealDataFastOr/dimension_033-32 1.10MB ± 0% 1.44MB ± 0% +30.92% (p=0.008 n=5+5) 61 | 62 | RealDataFastOr/dimension_008-32 537kB ± 0% 97kB ± 0% -81.94% (p=0.008 n=5+5) 63 | RealDataFastOr/census-income-32 187kB ± 0% 70kB ± 0% -62.86% (p=0.008 n=5+5) 64 | RealDataFastOr/census-income_srt-32 99.1kB ± 0% 69.6kB ± 0% -29.81% (p=0.008 n=5+5) 65 | RealDataFastOr/weather_sept_85_srt-32 375kB ± 0% 292kB ± 0% -21.95% (p=0.008 n=5+5) 66 | RealDataFastOr/uscensus2000-32 169kB ± 0% 231kB ± 0% +36.97% (p=0.008 n=5+5) 67 | RealDataFastOr/weather_sept_85-32 169kB ± 0% 292kB ± 0% +72.93% (p=0.008 n=5+5) 68 | 69 | name MEM_ALLOCS old allocs/op new allocs/op delta 70 | RealDataFastOr/census1881_srt-32 29.7k ± 0% 0.0k ± 0% -99.91% (p=0.008 n=5+5) 71 | RealDataFastOr/wikileaks-noquotes_srt-32 6.06k ± 0% 0.02k ± 0% -99.74% (p=0.008 n=5+5) 72 | RealDataFastOr/dimension_003-32 4.57k ± 0% 0.03k ± 2% -99.42% (p=0.008 n=5+5) 73 | RealDataFastOr/dimension_033-32 4.33k ± 0% 0.03k ± 0% -99.38% (p=0.000 n=5+4) 74 | RealDataFastOr/uscensus2000-32 1.75k ± 0% 0.06k ± 0% -96.85% (p=0.008 n=5+5) 75 | RealDataFastOr/dimension_008-32 704 ± 0% 23 ± 3% -96.79% (p=0.008 n=5+5) 76 | RealDataFastOr/census-income-32 271 ± 0% 9 ± 0% -96.68% (p=0.008 n=5+5) 77 | RealDataFastOr/weather_sept_85_srt-32 248 ± 0% 14 ± 0% -94.35% (p=0.008 n=5+5) 78 | RealDataFastOr/weather_sept_85-32 81.0 ± 0% 14.0 ± 0% -82.72% (p=0.008 n=5+5) 79 | RealDataFastOr/census-income_srt-32 40.0 ± 0% 9.0 ± 0% -77.50% (p=0.008 n=5+5) 80 | RealDataFastOr/census1881-32 54.5k ± 0% 0.0k ± 0% ~ (p=0.079 n=4+5) 81 | RealDataFastOr/wikileaks-noquotes-32 39.2k ± 0% 0.0k ± 0% ~ (p=0.079 n=4+5) 82 | ``` 83 | -------------------------------------------------------------------------------- /benchmark_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Dgraph Labs, Inc. and Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package sroar 18 | 19 | import ( 20 | "math/rand" 21 | "runtime" 22 | "testing" 23 | 24 | "github.com/RoaringBitmap/roaring/roaring64" 25 | ) 26 | 27 | // go test -bench BenchmarkMemoryUsage -run - 28 | func BenchmarkMemoryUsage(b *testing.B) { 29 | b.StopTimer() 30 | bitmaps := make([]*Bitmap, 0, 10) 31 | 32 | incr := uint64(1 << 16) 33 | max := uint64(1<<32 - 1) 34 | for x := 0; x < 10; x++ { 35 | rb := NewBitmap() 36 | 37 | var i uint64 38 | for i = 0; i <= max-incr; i += incr { 39 | rb.Set(i) 40 | } 41 | 42 | bitmaps = append(bitmaps, rb) 43 | } 44 | 45 | var stats runtime.MemStats 46 | runtime.ReadMemStats(&stats) 47 | b.Logf("HeapInUse: %d, HeapObjects: %d", stats.HeapInuse, stats.HeapObjects) 48 | b.StartTimer() 49 | } 50 | 51 | // go test -bench BenchmarkIntersection -run - 52 | func BenchmarkIntersectionRoaring(b *testing.B) { 53 | b.StopTimer() 54 | r := rand.New(rand.NewSource(0)) 55 | s1 := NewBitmap() 56 | sz := int64(150000) 57 | initsize := 65000 58 | for i := 0; i < initsize; i++ { 59 | s1.Set(uint64(r.Int63n(sz))) 60 | } 61 | 62 | s2 := NewBitmap() 63 | sz = int64(100000000) 64 | initsize = 65000 65 | for i := 0; i < initsize; i++ { 66 | s2.Set(uint64(r.Int63n((sz)))) 67 | } 68 | b.StartTimer() 69 | 70 | card := 0 71 | for j := 0; j < b.N; j++ { 72 | s3 := And(s1, s2) 73 | card = card + s3.GetCardinality() 74 | } 75 | b.Logf("card: %d\n", card) 76 | } 77 | 78 | // go test -bench BenchmarkSet -run - 79 | func BenchmarkSetRoaring(b *testing.B) { 80 | b.StopTimer() 81 | r := rand.New(rand.NewSource(0)) 82 | sz := int64(1000000) 83 | s := NewBitmap() 84 | b.StartTimer() 85 | for i := 0; i < b.N; i++ { 86 | s.Set(uint64(r.Int63n(sz))) 87 | } 88 | } 89 | 90 | func BenchmarkMerge10K(b *testing.B) { 91 | var bitmaps []*Bitmap 92 | for i := 0; i < 10000; i++ { 93 | bm := NewBitmap() 94 | for j := 0; j < 1000; j++ { 95 | x := rand.Uint64() % 1e8 // 10M. 96 | bm.Set(x) 97 | } 98 | bitmaps = append(bitmaps, bm) 99 | } 100 | 101 | second := func() *Bitmap { 102 | var res []*Bitmap 103 | for i := 0; i < 100; i += 1 { 104 | input := bitmaps[100*i : 100*i+100] 105 | out := FastOr(input...) 106 | res = append(res, out) 107 | } 108 | return FastOr(res...) 109 | } 110 | 111 | out := FastOr(bitmaps...) 112 | b.Logf("Out: %s\n", out) 113 | out2 := second() 114 | if out2.GetCardinality() != out.GetCardinality() { 115 | panic("Don't match") 116 | } 117 | out3 := FastParOr(8, bitmaps...) 118 | if out3.GetCardinality() != out.GetCardinality() { 119 | panic("Don't match") 120 | } 121 | b.Logf("card2: %d card3: %d", out2.GetCardinality(), out3.GetCardinality()) 122 | 123 | b.Run("fastor", func(b *testing.B) { 124 | for i := 0; i < b.N; i++ { 125 | _ = FastOr(bitmaps...) 126 | } 127 | }) 128 | 129 | b.Run("fastor-groups", func(b *testing.B) { 130 | for i := 0; i < b.N; i++ { 131 | _ = second() 132 | } 133 | }) 134 | b.Run("fastparor", func(b *testing.B) { 135 | for i := 0; i < b.N; i++ { 136 | _ = FastParOr(4, bitmaps...) 137 | } 138 | }) 139 | } 140 | 141 | func BenchmarkRemoveRange(b *testing.B) { 142 | bm := NewBitmap() 143 | N := uint64(1e5) 144 | for i := uint64(0); i < N; i++ { 145 | bm.Set(uint64(i)) 146 | } 147 | 148 | bench := func(b *testing.B, factor uint64) { 149 | sz := uint64(N / factor) 150 | cnt := uint64(N / sz) 151 | for j := 0; j < b.N; j++ { 152 | b.StopTimer() 153 | bm2 := bm.Clone() 154 | b.StartTimer() 155 | for i := uint64(0); i < cnt; i++ { 156 | bm2.RemoveRange(i*sz, (i+1)*sz) 157 | } 158 | } 159 | } 160 | b.Run("N/2", func(b *testing.B) { 161 | bench(b, 2) 162 | }) 163 | b.Run("N/4", func(b *testing.B) { 164 | bench(b, 4) 165 | }) 166 | b.Run("N/16", func(b *testing.B) { 167 | bench(b, 16) 168 | }) 169 | b.Run("N/256", func(b *testing.B) { 170 | bench(b, 256) 171 | }) 172 | } 173 | 174 | func BenchmarkRemoveRangeRoaring64(b *testing.B) { 175 | bm := roaring64.NewBitmap() 176 | N := uint64(1e5) 177 | for i := uint64(0); i < N; i++ { 178 | bm.Add(uint64(i)) 179 | } 180 | 181 | bench := func(b *testing.B, factor uint64) { 182 | sz := uint64(N / factor) 183 | cnt := uint64(N / sz) 184 | for j := 0; j < b.N; j++ { 185 | b.StopTimer() 186 | bm2 := bm.Clone() 187 | b.StartTimer() 188 | for i := uint64(0); i < cnt; i++ { 189 | bm2.RemoveRange(i*sz, (i+1)*sz) 190 | } 191 | } 192 | } 193 | b.Run("N/2", func(b *testing.B) { 194 | bench(b, 2) 195 | }) 196 | b.Run("N/4", func(b *testing.B) { 197 | bench(b, 4) 198 | }) 199 | b.Run("N/16", func(b *testing.B) { 200 | bench(b, 16) 201 | }) 202 | b.Run("N/256", func(b *testing.B) { 203 | bench(b, 256) 204 | }) 205 | } 206 | 207 | func BenchmarkSelectSroar(b *testing.B) { 208 | bm := NewBitmap() 209 | N := uint64(1e5) 210 | for i := uint64(0); i < N; i++ { 211 | bm.Set(uint64(i)) 212 | } 213 | 214 | b.ResetTimer() 215 | for i := 0; i < b.N; i++ { 216 | for j := uint64(0); j < N; j++ { 217 | bm.Select(j) 218 | } 219 | } 220 | } 221 | 222 | func BenchmarkSelectRoaring64(b *testing.B) { 223 | bm := roaring64.NewBitmap() 224 | N := uint64(1e5) 225 | for i := uint64(0); i < N; i++ { 226 | bm.Add(uint64(i)) 227 | } 228 | 229 | b.ResetTimer() 230 | for i := 0; i < b.N; i++ { 231 | for j := uint64(0); j < N; j++ { 232 | bm.Select(j) 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /benchmarks/README.md: -------------------------------------------------------------------------------- 1 | The benchmarks were run on Manish's desktop with AMD Threadripper 2950x. 2 | 3 | sroar.txt: Serialized Roaring Bitmaps 4 | roar64.txt: RoaringBitmap/roaring using roaring64. 5 | roar64-serial.txt: Same as above, but with extra step of Marshal after FastOr. 6 | roar32.txt: RoaringBitmap/roaring using 32-bit roaring. 7 | -------------------------------------------------------------------------------- /benchmarks/roar32.txt: -------------------------------------------------------------------------------- 1 | goos: linux 2 | goarch: amd64 3 | pkg: sroar 4 | BenchmarkRealDataFastOr/census-income_srt-32 3237 396483 ns/op 72296 B/op 30 allocs/op 5 | BenchmarkRealDataFastOr/census-income_srt-32 2907 424007 ns/op 72296 B/op 30 allocs/op 6 | BenchmarkRealDataFastOr/census-income_srt-32 3489 393445 ns/op 72297 B/op 30 allocs/op 7 | BenchmarkRealDataFastOr/census-income_srt-32 3208 404606 ns/op 72296 B/op 30 allocs/op 8 | BenchmarkRealDataFastOr/census-income_srt-32 3058 404708 ns/op 72296 B/op 30 allocs/op 9 | BenchmarkRealDataFastOr/census-income-32 1561 810697 ns/op 42504 B/op 24 allocs/op 10 | BenchmarkRealDataFastOr/census-income-32 1629 788560 ns/op 42504 B/op 24 allocs/op 11 | BenchmarkRealDataFastOr/census-income-32 1513 800652 ns/op 42504 B/op 24 allocs/op 12 | BenchmarkRealDataFastOr/census-income-32 1557 797843 ns/op 42504 B/op 24 allocs/op 13 | BenchmarkRealDataFastOr/census-income-32 1586 792257 ns/op 42504 B/op 24 allocs/op 14 | BenchmarkRealDataFastOr/census1881_srt-32 691 1706054 ns/op 822197 B/op 390 allocs/op 15 | BenchmarkRealDataFastOr/census1881_srt-32 710 1705790 ns/op 822198 B/op 390 allocs/op 16 | BenchmarkRealDataFastOr/census1881_srt-32 676 1695293 ns/op 822198 B/op 390 allocs/op 17 | BenchmarkRealDataFastOr/census1881_srt-32 686 1659075 ns/op 822197 B/op 390 allocs/op 18 | BenchmarkRealDataFastOr/census1881_srt-32 734 1697356 ns/op 822203 B/op 390 allocs/op 19 | BenchmarkRealDataFastOr/census1881-32 543 2118383 ns/op 602417 B/op 286 allocs/op 20 | BenchmarkRealDataFastOr/census1881-32 585 2062387 ns/op 602418 B/op 286 allocs/op 21 | BenchmarkRealDataFastOr/census1881-32 556 2033598 ns/op 602419 B/op 286 allocs/op 22 | BenchmarkRealDataFastOr/census1881-32 555 2040620 ns/op 602417 B/op 286 allocs/op 23 | BenchmarkRealDataFastOr/census1881-32 538 2074632 ns/op 602417 B/op 286 allocs/op 24 | BenchmarkRealDataFastOr/dimension_003-32 584 2037353 ns/op 500936 B/op 377 allocs/op 25 | BenchmarkRealDataFastOr/dimension_003-32 602 2099409 ns/op 500936 B/op 377 allocs/op 26 | BenchmarkRealDataFastOr/dimension_003-32 604 2113940 ns/op 500936 B/op 377 allocs/op 27 | BenchmarkRealDataFastOr/dimension_003-32 592 2104249 ns/op 500934 B/op 377 allocs/op 28 | BenchmarkRealDataFastOr/dimension_003-32 620 2069069 ns/op 500935 B/op 377 allocs/op 29 | BenchmarkRealDataFastOr/dimension_008-32 1005 1206154 ns/op 456252 B/op 339 allocs/op 30 | BenchmarkRealDataFastOr/dimension_008-32 1018 1174863 ns/op 456251 B/op 339 allocs/op 31 | BenchmarkRealDataFastOr/dimension_008-32 1017 1191431 ns/op 456251 B/op 339 allocs/op 32 | BenchmarkRealDataFastOr/dimension_008-32 1006 1185149 ns/op 456252 B/op 339 allocs/op 33 | BenchmarkRealDataFastOr/dimension_008-32 1004 1169217 ns/op 456251 B/op 339 allocs/op 34 | BenchmarkRealDataFastOr/dimension_033-32 1022 1182652 ns/op 877751 B/op 377 allocs/op 35 | BenchmarkRealDataFastOr/dimension_033-32 981 1222996 ns/op 877749 B/op 377 allocs/op 36 | BenchmarkRealDataFastOr/dimension_033-32 996 1214407 ns/op 877751 B/op 377 allocs/op 37 | BenchmarkRealDataFastOr/dimension_033-32 981 1211939 ns/op 877750 B/op 377 allocs/op 38 | BenchmarkRealDataFastOr/dimension_033-32 1009 1252861 ns/op 877752 B/op 377 allocs/op 39 | BenchmarkRealDataFastOr/uscensus2000-32 262 4635955 ns/op 3923291 B/op 2989 allocs/op 40 | BenchmarkRealDataFastOr/uscensus2000-32 255 4810886 ns/op 3923287 B/op 2989 allocs/op 41 | BenchmarkRealDataFastOr/uscensus2000-32 249 4722452 ns/op 3923290 B/op 2989 allocs/op 42 | BenchmarkRealDataFastOr/uscensus2000-32 253 4582865 ns/op 3923298 B/op 2989 allocs/op 43 | BenchmarkRealDataFastOr/uscensus2000-32 268 4659114 ns/op 3923294 B/op 2989 allocs/op 44 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 3187 490599 ns/op 248066 B/op 105 allocs/op 45 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 2988 495712 ns/op 248066 B/op 105 allocs/op 46 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 3049 472503 ns/op 248067 B/op 105 allocs/op 47 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 3000 463048 ns/op 248066 B/op 105 allocs/op 48 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 3218 481629 ns/op 248066 B/op 105 allocs/op 49 | BenchmarkRealDataFastOr/weather_sept_85-32 346 3839449 ns/op 141064 B/op 75 allocs/op 50 | BenchmarkRealDataFastOr/weather_sept_85-32 318 3701353 ns/op 141064 B/op 75 allocs/op 51 | BenchmarkRealDataFastOr/weather_sept_85-32 327 3774119 ns/op 141064 B/op 75 allocs/op 52 | BenchmarkRealDataFastOr/weather_sept_85-32 324 3621583 ns/op 141064 B/op 75 allocs/op 53 | BenchmarkRealDataFastOr/weather_sept_85-32 314 3644309 ns/op 141064 B/op 75 allocs/op 54 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 3172 343294 ns/op 211392 B/op 106 allocs/op 55 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 3439 330383 ns/op 211392 B/op 106 allocs/op 56 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 3422 333312 ns/op 211392 B/op 106 allocs/op 57 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 3289 345704 ns/op 211392 B/op 106 allocs/op 58 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 3462 343257 ns/op 211392 B/op 106 allocs/op 59 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 2470 503213 ns/op 195057 B/op 100 allocs/op 60 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 2641 483767 ns/op 195057 B/op 100 allocs/op 61 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 2362 501118 ns/op 195056 B/op 100 allocs/op 62 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 2376 487647 ns/op 195056 B/op 100 allocs/op 63 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 2187 486348 ns/op 195056 B/op 100 allocs/op 64 | PASS 65 | ok sroar 228.238s 66 | -------------------------------------------------------------------------------- /benchmarks/roar64-serial.txt: -------------------------------------------------------------------------------- 1 | goos: linux 2 | goarch: amd64 3 | pkg: sroar 4 | BenchmarkRealDataFastOr/census-income_srt-32 0 NaN ns/op 0 B/op 0 allocs/op 5 | BenchmarkRealDataFastOr/census-income_srt-32 0 NaN ns/op 0 B/op 0 allocs/op 6 | BenchmarkRealDataFastOr/census-income_srt-32 0 NaN ns/op 0 B/op 0 allocs/op 7 | BenchmarkRealDataFastOr/census-income_srt-32 0 NaN ns/op 0 B/op 0 allocs/op 8 | BenchmarkRealDataFastOr/census-income_srt-32 0 NaN ns/op 0 B/op 0 allocs/op 9 | BenchmarkRealDataFastOr/census-income-32 675 1730524 ns/op 188072 B/op 285 allocs/op 10 | BenchmarkRealDataFastOr/census-income-32 697 1703823 ns/op 188072 B/op 285 allocs/op 11 | BenchmarkRealDataFastOr/census-income-32 691 1733674 ns/op 188072 B/op 285 allocs/op 12 | BenchmarkRealDataFastOr/census-income-32 741 1699996 ns/op 188078 B/op 285 allocs/op 13 | BenchmarkRealDataFastOr/census-income-32 736 1734198 ns/op 188072 B/op 285 allocs/op 14 | BenchmarkRealDataFastOr/census1881_srt-32 28 39004246 ns/op 16151822 B/op 29784 allocs/op 15 | BenchmarkRealDataFastOr/census1881_srt-32 27 38670935 ns/op 16151831 B/op 29784 allocs/op 16 | BenchmarkRealDataFastOr/census1881_srt-32 30 40208028 ns/op 16151835 B/op 29784 allocs/op 17 | BenchmarkRealDataFastOr/census1881_srt-32 31 39402403 ns/op 16151812 B/op 29784 allocs/op 18 | BenchmarkRealDataFastOr/census1881_srt-32 32 39176979 ns/op 16151907 B/op 29784 allocs/op 19 | BenchmarkRealDataFastOr/census1881-32 4 299804709 ns/op 588239388 B/op 54492 allocs/op 20 | BenchmarkRealDataFastOr/census1881-32 4 298464086 ns/op 588239520 B/op 54493 allocs/op 21 | BenchmarkRealDataFastOr/census1881-32 4 307197439 ns/op 588239490 B/op 54495 allocs/op 22 | BenchmarkRealDataFastOr/census1881-32 4 299339661 ns/op 588239550 B/op 54495 allocs/op 23 | BenchmarkRealDataFastOr/census1881-32 4 307566590 ns/op 588239154 B/op 54489 allocs/op 24 | BenchmarkRealDataFastOr/dimension_003-32 26 50447642 ns/op 7784190 B/op 4637 allocs/op 25 | BenchmarkRealDataFastOr/dimension_003-32 28 50519106 ns/op 7784153 B/op 4637 allocs/op 26 | BenchmarkRealDataFastOr/dimension_003-32 27 49900315 ns/op 7784161 B/op 4637 allocs/op 27 | BenchmarkRealDataFastOr/dimension_003-32 21 50628238 ns/op 7784176 B/op 4637 allocs/op 28 | BenchmarkRealDataFastOr/dimension_003-32 28 50919093 ns/op 7784145 B/op 4637 allocs/op 29 | BenchmarkRealDataFastOr/dimension_008-32 116 10078690 ns/op 568973 B/op 718 allocs/op 30 | BenchmarkRealDataFastOr/dimension_008-32 116 10143405 ns/op 568970 B/op 718 allocs/op 31 | BenchmarkRealDataFastOr/dimension_008-32 116 10364534 ns/op 568972 B/op 718 allocs/op 32 | BenchmarkRealDataFastOr/dimension_008-32 100 10073601 ns/op 568976 B/op 718 allocs/op 33 | BenchmarkRealDataFastOr/dimension_008-32 120 10304911 ns/op 568974 B/op 718 allocs/op 34 | BenchmarkRealDataFastOr/dimension_033-32 24 55936054 ns/op 1103823 B/op 4405 allocs/op 35 | BenchmarkRealDataFastOr/dimension_033-32 22 58310259 ns/op 1103826 B/op 4405 allocs/op 36 | BenchmarkRealDataFastOr/dimension_033-32 24 56238602 ns/op 1103825 B/op 4405 allocs/op 37 | BenchmarkRealDataFastOr/dimension_033-32 22 54599868 ns/op 1103820 B/op 4405 allocs/op 38 | BenchmarkRealDataFastOr/dimension_033-32 22 55728214 ns/op 1103822 B/op 4405 allocs/op 39 | BenchmarkRealDataFastOr/uscensus2000-32 1854 639099 ns/op 207769 B/op 1761 allocs/op 40 | BenchmarkRealDataFastOr/uscensus2000-32 1869 649343 ns/op 207769 B/op 1761 allocs/op 41 | BenchmarkRealDataFastOr/uscensus2000-32 1981 636494 ns/op 207769 B/op 1761 allocs/op 42 | BenchmarkRealDataFastOr/uscensus2000-32 1819 626103 ns/op 207769 B/op 1761 allocs/op 43 | BenchmarkRealDataFastOr/uscensus2000-32 1888 627919 ns/op 207769 B/op 1761 allocs/op 44 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 204 6416586 ns/op 375705 B/op 274 allocs/op 45 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 210 5999277 ns/op 375706 B/op 274 allocs/op 46 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 188 6205278 ns/op 375704 B/op 274 allocs/op 47 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 195 6072065 ns/op 375705 B/op 274 allocs/op 48 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 211 5979967 ns/op 375705 B/op 274 allocs/op 49 | BenchmarkRealDataFastOr/weather_sept_85-32 502 2327105 ns/op 179656 B/op 107 allocs/op 50 | BenchmarkRealDataFastOr/weather_sept_85-32 522 2347670 ns/op 179656 B/op 107 allocs/op 51 | BenchmarkRealDataFastOr/weather_sept_85-32 525 2345562 ns/op 179656 B/op 107 allocs/op 52 | BenchmarkRealDataFastOr/weather_sept_85-32 544 2327343 ns/op 179656 B/op 107 allocs/op 53 | BenchmarkRealDataFastOr/weather_sept_85-32 526 2302007 ns/op 179656 B/op 107 allocs/op 54 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 33 34783231 ns/op 23337651 B/op 6070 allocs/op 55 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 33 35360049 ns/op 23337632 B/op 6070 allocs/op 56 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 33 36466398 ns/op 23337620 B/op 6070 allocs/op 57 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 33 35570325 ns/op 23337648 B/op 6071 allocs/op 58 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 34 35819794 ns/op 23337625 B/op 6070 allocs/op 59 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 15 78689872 ns/op 76908907 B/op 39264 allocs/op 60 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 15 76461830 ns/op 76908972 B/op 39265 allocs/op 61 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 14 77407616 ns/op 76908937 B/op 39265 allocs/op 62 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 14 76442207 ns/op 76908915 B/op 39265 allocs/op 63 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 15 79325273 ns/op 76908939 B/op 39265 allocs/op 64 | PASS 65 | ok sroar 232.580s 66 | -------------------------------------------------------------------------------- /benchmarks/roar64.txt: -------------------------------------------------------------------------------- 1 | goos: linux 2 | goarch: amd64 3 | pkg: sroar 4 | BenchmarkRealDataFastOr/census-income_srt-32 5630 268013 ns/op 99120 B/op 40 allocs/op 5 | BenchmarkRealDataFastOr/census-income_srt-32 6331 259073 ns/op 99120 B/op 40 allocs/op 6 | BenchmarkRealDataFastOr/census-income_srt-32 4681 269383 ns/op 99120 B/op 40 allocs/op 7 | BenchmarkRealDataFastOr/census-income_srt-32 5035 250800 ns/op 99120 B/op 40 allocs/op 8 | BenchmarkRealDataFastOr/census-income_srt-32 6513 252922 ns/op 99120 B/op 40 allocs/op 9 | BenchmarkRealDataFastOr/census-income-32 700 1724748 ns/op 187288 B/op 271 allocs/op 10 | BenchmarkRealDataFastOr/census-income-32 691 1733193 ns/op 187288 B/op 271 allocs/op 11 | BenchmarkRealDataFastOr/census-income-32 714 1727127 ns/op 187288 B/op 271 allocs/op 12 | BenchmarkRealDataFastOr/census-income-32 748 1644983 ns/op 187288 B/op 271 allocs/op 13 | BenchmarkRealDataFastOr/census-income-32 660 1675496 ns/op 187288 B/op 271 allocs/op 14 | BenchmarkRealDataFastOr/census1881_srt-32 32 35608957 ns/op 15291023 B/op 29725 allocs/op 15 | BenchmarkRealDataFastOr/census1881_srt-32 31 36750266 ns/op 15290956 B/op 29725 allocs/op 16 | BenchmarkRealDataFastOr/census1881_srt-32 33 36986714 ns/op 15291013 B/op 29725 allocs/op 17 | BenchmarkRealDataFastOr/census1881_srt-32 34 37447836 ns/op 15290937 B/op 29725 allocs/op 18 | BenchmarkRealDataFastOr/census1881_srt-32 30 36991856 ns/op 15290958 B/op 29725 allocs/op 19 | BenchmarkRealDataFastOr/census1881-32 4 298795399 ns/op 585251670 B/op 54471 allocs/op 20 | BenchmarkRealDataFastOr/census1881-32 4 300873453 ns/op 585251480 B/op 54469 allocs/op 21 | BenchmarkRealDataFastOr/census1881-32 4 300399952 ns/op 585251130 B/op 54469 allocs/op 22 | BenchmarkRealDataFastOr/census1881-32 4 300566860 ns/op 585251416 B/op 54469 allocs/op 23 | BenchmarkRealDataFastOr/census1881-32 4 307401908 ns/op 585251168 B/op 54469 allocs/op 24 | BenchmarkRealDataFastOr/dimension_003-32 25 50285977 ns/op 7780704 B/op 4566 allocs/op 25 | BenchmarkRealDataFastOr/dimension_003-32 26 50364568 ns/op 7780765 B/op 4566 allocs/op 26 | BenchmarkRealDataFastOr/dimension_003-32 26 50644328 ns/op 7780700 B/op 4566 allocs/op 27 | BenchmarkRealDataFastOr/dimension_003-32 26 50380849 ns/op 7780694 B/op 4566 allocs/op 28 | BenchmarkRealDataFastOr/dimension_003-32 28 50253489 ns/op 7780716 B/op 4566 allocs/op 29 | BenchmarkRealDataFastOr/dimension_008-32 100 10217254 ns/op 537326 B/op 704 allocs/op 30 | BenchmarkRealDataFastOr/dimension_008-32 100 10043920 ns/op 537327 B/op 704 allocs/op 31 | BenchmarkRealDataFastOr/dimension_008-32 118 9799420 ns/op 537324 B/op 704 allocs/op 32 | BenchmarkRealDataFastOr/dimension_008-32 122 10012414 ns/op 537327 B/op 704 allocs/op 33 | BenchmarkRealDataFastOr/dimension_008-32 100 10163465 ns/op 537328 B/op 704 allocs/op 34 | BenchmarkRealDataFastOr/dimension_033-32 21 53590811 ns/op 1100359 B/op 4334 allocs/op 35 | BenchmarkRealDataFastOr/dimension_033-32 22 56343225 ns/op 1100354 B/op 4334 allocs/op 36 | BenchmarkRealDataFastOr/dimension_033-32 20 54287116 ns/op 1100359 B/op 4334 allocs/op 37 | BenchmarkRealDataFastOr/dimension_033-32 21 55556688 ns/op 1100353 B/op 4334 allocs/op 38 | BenchmarkRealDataFastOr/dimension_033-32 22 55182267 ns/op 1100354 B/op 4334 allocs/op 39 | BenchmarkRealDataFastOr/uscensus2000-32 2286 550714 ns/op 168889 B/op 1748 allocs/op 40 | BenchmarkRealDataFastOr/uscensus2000-32 2214 562883 ns/op 168889 B/op 1748 allocs/op 41 | BenchmarkRealDataFastOr/uscensus2000-32 2666 564394 ns/op 168889 B/op 1748 allocs/op 42 | BenchmarkRealDataFastOr/uscensus2000-32 2398 557926 ns/op 168889 B/op 1748 allocs/op 43 | BenchmarkRealDataFastOr/uscensus2000-32 2244 544473 ns/op 168889 B/op 1748 allocs/op 44 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 198 6179149 ns/op 374594 B/op 248 allocs/op 45 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 201 6026161 ns/op 374594 B/op 248 allocs/op 46 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 202 5930852 ns/op 374593 B/op 248 allocs/op 47 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 205 6318674 ns/op 374593 B/op 248 allocs/op 48 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 184 6197007 ns/op 374594 B/op 248 allocs/op 49 | BenchmarkRealDataFastOr/weather_sept_85-32 544 2291202 ns/op 169072 B/op 81 allocs/op 50 | BenchmarkRealDataFastOr/weather_sept_85-32 505 2261783 ns/op 169072 B/op 81 allocs/op 51 | BenchmarkRealDataFastOr/weather_sept_85-32 510 2251110 ns/op 169072 B/op 81 allocs/op 52 | BenchmarkRealDataFastOr/weather_sept_85-32 537 2325324 ns/op 169072 B/op 81 allocs/op 53 | BenchmarkRealDataFastOr/weather_sept_85-32 512 2281204 ns/op 169072 B/op 81 allocs/op 54 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 33 35475244 ns/op 22841103 B/op 6055 allocs/op 55 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 32 35497821 ns/op 22841094 B/op 6055 allocs/op 56 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 34 34964536 ns/op 22841096 B/op 6055 allocs/op 57 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 34 34965517 ns/op 22841108 B/op 6056 allocs/op 58 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 37 32875087 ns/op 22841106 B/op 6056 allocs/op 59 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 14 77248690 ns/op 76312402 B/op 39229 allocs/op 60 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 15 76338200 ns/op 76312400 B/op 39229 allocs/op 61 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 15 76487081 ns/op 76312377 B/op 39229 allocs/op 62 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 15 76758214 ns/op 76312311 B/op 39227 allocs/op 63 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 15 75565626 ns/op 76312442 B/op 39229 allocs/op 64 | PASS 65 | ok sroar 232.580s 66 | -------------------------------------------------------------------------------- /benchmarks/sroar.txt: -------------------------------------------------------------------------------- 1 | goos: linux 2 | goarch: amd64 3 | pkg: sroar 4 | BenchmarkRealDataFastOr/census-income_srt-32 1231 985441 ns/op 69568 B/op 9 allocs/op 5 | BenchmarkRealDataFastOr/census-income_srt-32 1170 970877 ns/op 69568 B/op 9 allocs/op 6 | BenchmarkRealDataFastOr/census-income_srt-32 1142 991416 ns/op 69568 B/op 9 allocs/op 7 | BenchmarkRealDataFastOr/census-income_srt-32 1234 994570 ns/op 69568 B/op 9 allocs/op 8 | BenchmarkRealDataFastOr/census-income_srt-32 1138 986533 ns/op 69568 B/op 9 allocs/op 9 | BenchmarkRealDataFastOr/census-income-32 1176 1045625 ns/op 69568 B/op 9 allocs/op 10 | BenchmarkRealDataFastOr/census-income-32 1076 1046500 ns/op 69568 B/op 9 allocs/op 11 | BenchmarkRealDataFastOr/census-income-32 1230 1035795 ns/op 69568 B/op 9 allocs/op 12 | BenchmarkRealDataFastOr/census-income-32 1146 1047617 ns/op 69568 B/op 9 allocs/op 13 | BenchmarkRealDataFastOr/census-income-32 1124 1053217 ns/op 69568 B/op 9 allocs/op 14 | BenchmarkRealDataFastOr/census1881_srt-32 415 2909354 ns/op 1440531 B/op 27 allocs/op 15 | BenchmarkRealDataFastOr/census1881_srt-32 417 2895406 ns/op 1440536 B/op 27 allocs/op 16 | BenchmarkRealDataFastOr/census1881_srt-32 412 2861475 ns/op 1440552 B/op 27 allocs/op 17 | BenchmarkRealDataFastOr/census1881_srt-32 409 2890501 ns/op 1440531 B/op 27 allocs/op 18 | BenchmarkRealDataFastOr/census1881_srt-32 412 2901987 ns/op 1440539 B/op 27 allocs/op 19 | BenchmarkRealDataFastOr/census1881-32 524 2171016 ns/op 1440539 B/op 27 allocs/op 20 | BenchmarkRealDataFastOr/census1881-32 554 2095909 ns/op 1440534 B/op 27 allocs/op 21 | BenchmarkRealDataFastOr/census1881-32 552 2098805 ns/op 1440538 B/op 27 allocs/op 22 | BenchmarkRealDataFastOr/census1881-32 583 2212162 ns/op 1440532 B/op 27 allocs/op 23 | BenchmarkRealDataFastOr/census1881-32 554 2161902 ns/op 1440528 B/op 27 allocs/op 24 | BenchmarkRealDataFastOr/dimension_003-32 100 11520684 ns/op 1440539 B/op 27 allocs/op 25 | BenchmarkRealDataFastOr/dimension_003-32 97 11662447 ns/op 1440521 B/op 26 allocs/op 26 | BenchmarkRealDataFastOr/dimension_003-32 97 11814515 ns/op 1440503 B/op 26 allocs/op 27 | BenchmarkRealDataFastOr/dimension_003-32 97 11060395 ns/op 1440536 B/op 27 allocs/op 28 | BenchmarkRealDataFastOr/dimension_003-32 98 11745283 ns/op 1440544 B/op 27 allocs/op 29 | BenchmarkRealDataFastOr/dimension_008-32 322 3725925 ns/op 97035 B/op 22 allocs/op 30 | BenchmarkRealDataFastOr/dimension_008-32 319 3770389 ns/op 97063 B/op 23 allocs/op 31 | BenchmarkRealDataFastOr/dimension_008-32 321 3780069 ns/op 97066 B/op 23 allocs/op 32 | BenchmarkRealDataFastOr/dimension_008-32 320 3783169 ns/op 97050 B/op 23 allocs/op 33 | BenchmarkRealDataFastOr/dimension_008-32 334 3681594 ns/op 97039 B/op 22 allocs/op 34 | BenchmarkRealDataFastOr/dimension_033-32 453 2662471 ns/op 1440537 B/op 27 allocs/op 35 | BenchmarkRealDataFastOr/dimension_033-32 446 2661381 ns/op 1440531 B/op 27 allocs/op 36 | BenchmarkRealDataFastOr/dimension_033-32 458 2663479 ns/op 1440522 B/op 26 allocs/op 37 | BenchmarkRealDataFastOr/dimension_033-32 446 2640647 ns/op 1440546 B/op 27 allocs/op 38 | BenchmarkRealDataFastOr/dimension_033-32 440 2662052 ns/op 1440528 B/op 27 allocs/op 39 | BenchmarkRealDataFastOr/uscensus2000-32 1490 798571 ns/op 231328 B/op 55 allocs/op 40 | BenchmarkRealDataFastOr/uscensus2000-32 1548 788395 ns/op 231335 B/op 55 allocs/op 41 | BenchmarkRealDataFastOr/uscensus2000-32 1574 783830 ns/op 231341 B/op 55 allocs/op 42 | BenchmarkRealDataFastOr/uscensus2000-32 1497 792042 ns/op 231332 B/op 55 allocs/op 43 | BenchmarkRealDataFastOr/uscensus2000-32 1615 790154 ns/op 231313 B/op 55 allocs/op 44 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 447 2688981 ns/op 292369 B/op 14 allocs/op 45 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 444 2760343 ns/op 292369 B/op 14 allocs/op 46 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 441 2701262 ns/op 292371 B/op 14 allocs/op 47 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 435 2714158 ns/op 292371 B/op 14 allocs/op 48 | BenchmarkRealDataFastOr/weather_sept_85_srt-32 458 2726435 ns/op 292369 B/op 14 allocs/op 49 | BenchmarkRealDataFastOr/weather_sept_85-32 286 4012349 ns/op 292380 B/op 14 allocs/op 50 | BenchmarkRealDataFastOr/weather_sept_85-32 294 4108362 ns/op 292368 B/op 14 allocs/op 51 | BenchmarkRealDataFastOr/weather_sept_85-32 303 4111663 ns/op 292369 B/op 14 allocs/op 52 | BenchmarkRealDataFastOr/weather_sept_85-32 292 4036059 ns/op 292373 B/op 14 allocs/op 53 | BenchmarkRealDataFastOr/weather_sept_85-32 285 4101576 ns/op 292370 B/op 14 allocs/op 54 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 1125 1024101 ns/op 579116 B/op 16 allocs/op 55 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 1219 1015759 ns/op 579124 B/op 16 allocs/op 56 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 1156 1031406 ns/op 579123 B/op 16 allocs/op 57 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 1072 1027132 ns/op 579119 B/op 16 allocs/op 58 | BenchmarkRealDataFastOr/wikileaks-noquotes_srt-32 1158 997450 ns/op 579112 B/op 16 allocs/op 59 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 1309 888115 ns/op 579125 B/op 16 allocs/op 60 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 1442 896740 ns/op 579117 B/op 16 allocs/op 61 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 1260 893850 ns/op 579123 B/op 16 allocs/op 62 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 1314 908210 ns/op 579122 B/op 16 allocs/op 63 | BenchmarkRealDataFastOr/wikileaks-noquotes-32 1422 905125 ns/op 579121 B/op 16 allocs/op 64 | PASS 65 | ok sroar 289.943s 66 | -------------------------------------------------------------------------------- /bitmap.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Dgraph Labs, Inc. and Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package sroar 18 | 19 | import ( 20 | "fmt" 21 | "math" 22 | "sort" 23 | "strings" 24 | "sync" 25 | 26 | "github.com/pkg/errors" 27 | ) 28 | 29 | var empty = make([]uint16, 16<<20) 30 | 31 | const mask = uint64(0xFFFFFFFFFFFF0000) 32 | 33 | type Bitmap struct { 34 | data []uint16 35 | keys node 36 | 37 | // This _ptr is only used when we start with a []byte instead of a 38 | // []uint16. Because we do an unsafe conversion to []uint16 data, and hence, 39 | // do NOT own a valid pointer to the underlying array. 40 | _ptr []byte 41 | 42 | // memMoved keeps track of how many uint16 moves we had to do. The smaller 43 | // this number, the more efficient we have been. 44 | memMoved int 45 | } 46 | 47 | // FromBuffer returns a pointer to bitmap corresponding to the given buffer. This bitmap shouldn't 48 | // be modified because it might corrupt the given buffer. 49 | func FromBuffer(data []byte) *Bitmap { 50 | assert(len(data)%2 == 0) 51 | if len(data) < 8 { 52 | return NewBitmap() 53 | } 54 | du := toUint16Slice(data) 55 | x := toUint64Slice(du[:4])[indexNodeSize] 56 | return &Bitmap{ 57 | data: du, 58 | _ptr: data, // Keep a hold of data, otherwise GC would do its thing. 59 | keys: toUint64Slice(du[:x]), 60 | } 61 | } 62 | 63 | // FromBufferWithCopy creates a copy of the given buffer and returns a bitmap based on the copied 64 | // buffer. This bitmap is safe for both read and write operations. 65 | func FromBufferWithCopy(src []byte) *Bitmap { 66 | assert(len(src)%2 == 0) 67 | if len(src) < 8 { 68 | return NewBitmap() 69 | } 70 | src16 := toUint16Slice(src) 71 | dst16 := make([]uint16, len(src16)) 72 | copy(dst16, src16) 73 | x := toUint64Slice(dst16[:4])[indexNodeSize] 74 | 75 | return &Bitmap{ 76 | data: dst16, 77 | keys: toUint64Slice(dst16[:x]), 78 | } 79 | } 80 | 81 | func (ra *Bitmap) ToBuffer() []byte { 82 | if ra.IsEmpty() { 83 | return nil 84 | } 85 | return toByteSlice(ra.data) 86 | } 87 | 88 | func (ra *Bitmap) ToBufferWithCopy() []byte { 89 | if ra.IsEmpty() { 90 | return nil 91 | } 92 | buf := make([]uint16, len(ra.data)) 93 | copy(buf, ra.data) 94 | return toByteSlice(buf) 95 | } 96 | 97 | func NewBitmap() *Bitmap { 98 | return NewBitmapWith(2) 99 | } 100 | 101 | func NewBitmapWith(numKeys int) *Bitmap { 102 | if numKeys < 2 { 103 | panic("Must contain at least two keys.") 104 | } 105 | ra := &Bitmap{ 106 | // Each key must also keep an offset. So, we need to double the number 107 | // of uint64s allocated. Plus, we need to make space for the first 2 108 | // uint64s to store the number of keys and node size. 109 | data: make([]uint16, 4*(2*numKeys+2)), 110 | } 111 | ra.keys = toUint64Slice(ra.data) 112 | ra.keys.setNodeSize(len(ra.data)) 113 | 114 | // Always generate a container for key = 0x00. Otherwise, node gets confused 115 | // about whether a zero key is a new key or not. 116 | offset := ra.newContainer(minContainerSize) 117 | // First two are for num keys. index=2 -> 0 key. index=3 -> offset. 118 | ra.keys.setAt(indexNodeStart+1, offset) 119 | ra.keys.setNumKeys(1) 120 | 121 | return ra 122 | } 123 | 124 | func (ra *Bitmap) initSpaceForKeys(N int) { 125 | if N == 0 { 126 | return 127 | } 128 | curSize := uint64(len(ra.keys) * 4) // U64 -> U16 129 | bySize := uint64(N * 8) // 2xU64 (key, value) -> 2x4xU16 130 | 131 | // The following code is borrowed from setKey. 132 | ra.scootRight(curSize, bySize) 133 | ra.keys = toUint64Slice(ra.data[:curSize+bySize]) 134 | ra.keys.setNodeSize(int(curSize + bySize)) 135 | assert(1 == ra.keys.numKeys()) // This initialization assumes that the number of keys are 1. 136 | 137 | // The containers have moved to the right bySize. So, update their offsets. 138 | // Currently, there's only one container. 139 | val := ra.keys.val(0) 140 | ra.keys.setAt(valOffset(0), val+uint64(bySize)) 141 | } 142 | 143 | // setKey sets a key and container offset. 144 | func (ra *Bitmap) setKey(k uint64, offset uint64) uint64 { 145 | if added := ra.keys.set(k, offset); !added { 146 | // No new key was added. So, we can just return. 147 | return offset 148 | } 149 | // A new key was added. Let's ensure that ra.keys is not full. 150 | if !ra.keys.isFull() { 151 | return offset 152 | } 153 | 154 | // ra.keys is full. We should expand its size. 155 | curSize := uint64(len(ra.keys) * 4) // Multiply by 4 for U64 -> U16. 156 | bySize := curSize 157 | if bySize > math.MaxUint16 { 158 | bySize = math.MaxUint16 159 | } 160 | 161 | ra.scootRight(curSize, bySize) 162 | ra.keys = toUint64Slice(ra.data[:curSize+bySize]) 163 | ra.keys.setNodeSize(int(curSize + bySize)) 164 | 165 | // All containers have moved to the right by bySize bytes. 166 | // Update their offsets. 167 | n := ra.keys 168 | for i := 0; i < n.maxKeys(); i++ { 169 | val := n.val(i) 170 | if val > 0 { 171 | n.setAt(valOffset(i), val+uint64(bySize)) 172 | } 173 | } 174 | return offset + bySize 175 | } 176 | 177 | func (ra *Bitmap) fastExpand(bySize uint64) { 178 | prev := len(ra.keys) * 4 // Multiply by 4 to convert from u16 to u64. 179 | 180 | // This following statement also works. But, given how much fastExpand gets 181 | // called (a lot), probably better to control allocation. 182 | // ra.data = append(ra.data, empty[:bySize]...) 183 | 184 | toSize := len(ra.data) + int(bySize) 185 | if toSize <= cap(ra.data) { 186 | ra.data = ra.data[:toSize] 187 | return 188 | } 189 | growBy := cap(ra.data) 190 | if growBy < int(bySize) { 191 | growBy = int(bySize) 192 | } 193 | out := make([]uint16, cap(ra.data)+growBy) 194 | copy(out, ra.data) 195 | ra.data = out[:toSize] 196 | ra._ptr = nil // Allow Go to GC whatever this was pointing to. 197 | // Re-reference ra.keys correctly because underlying array has changed. 198 | ra.keys = toUint64Slice(ra.data[:prev]) 199 | } 200 | 201 | // scootRight isn't aware of containers. It's going to create empty space of 202 | // bySize at the given offset in ra.data. The offset doesn't need to line up 203 | // with a container. 204 | func (ra *Bitmap) scootRight(offset uint64, bySize uint64) { 205 | left := ra.data[offset:] 206 | 207 | ra.fastExpand(bySize) // Expand the buffer. 208 | right := ra.data[len(ra.data)-len(left):] 209 | n := copy(right, left) // Move data right. 210 | ra.memMoved += n 211 | 212 | Memclr(ra.data[offset : offset+uint64(bySize)]) // Zero out the space in the middle. 213 | } 214 | 215 | // scootLeft removes size number of uint16s starting from the given offset. 216 | func (ra *Bitmap) scootLeft(offset uint64, size uint64) { 217 | n := uint64(len(ra.data)) 218 | right := ra.data[offset+size:] 219 | ra.memMoved += copy(ra.data[offset:], right) 220 | ra.data = ra.data[:n-size] 221 | } 222 | 223 | func (ra *Bitmap) newContainer(sz uint16) uint64 { 224 | offset := uint64(len(ra.data)) 225 | ra.fastExpand(uint64(sz)) 226 | Memclr(ra.data[offset : offset+uint64(sz)]) 227 | ra.data[offset] = sz 228 | return offset 229 | } 230 | 231 | // expandContainer would expand a container at the given offset. It would typically double the size 232 | // of the container, until it reaches a threshold, where the size of the container would reach 2^16. 233 | // Expressed in uint16s, that'd be (2^16)/(2^4) = 2^12 = 4096. So, if the container size >= 2048, 234 | // then doubling that would put it above 4096. That's why in the code below, you see the checks for 235 | // size 2048. 236 | func (ra *Bitmap) expandContainer(offset uint64) { 237 | sz := ra.data[offset] 238 | if sz == 0 { 239 | panic("Container size should NOT be zero") 240 | } 241 | bySize := uint16(sz) 242 | if sz >= 2048 { 243 | // Size is in uint16. Half of max allowed size. If we're expanding the container by more 244 | // than 2048, we should just cap it to max size of 4096. 245 | assert(sz < maxContainerSize) 246 | bySize = maxContainerSize - sz 247 | } 248 | 249 | // Select the portion to the right of the container, beyond its right boundary. 250 | ra.scootRight(offset+uint64(sz), uint64(bySize)) 251 | ra.keys.updateOffsets(offset, uint64(bySize), true) 252 | 253 | if sz < 2048 { 254 | ra.data[offset] = sz + bySize 255 | 256 | } else { 257 | // Convert to bitmap container. 258 | src := array(ra.getContainer(offset)) 259 | buf := src.toBitmapContainer(nil) 260 | assert(copy(ra.data[offset:], buf) == maxContainerSize) 261 | } 262 | } 263 | 264 | // stepSize is used for container expansion. For a container of given size n, 265 | // stepSize would return the target size. This function is used to reduce the 266 | // number of times expansion needs to happen for each container. 267 | func stepSize(n uint16) uint16 { 268 | // <=64 -> 128 269 | // <=128 -> 256 270 | // <=256 -> 512 271 | // <=512 -> 1024 272 | // <=1024 -> 2048 273 | // >1024 -> maxSize (convert to bitmap) 274 | for i := uint16(64); i <= 1024; i *= 2 { 275 | if n <= i { 276 | return i * 2 277 | } 278 | } 279 | return maxContainerSize 280 | } 281 | 282 | // copyAt would copy over a given container via src, into the container at 283 | // offset. If src is a bitmap, it would copy it over directly. If src is an 284 | // array container, then it would follow these paths: 285 | // - If src is smaller than dst, copy it over. 286 | // - If not, look for target size for dst using the stepSize function. 287 | // - If target size is maxSize, then convert src to a bitmap container, and 288 | // copy to dst. 289 | // - If target size is not max size, then expand dst container and copy src. 290 | func (ra *Bitmap) copyAt(offset uint64, src []uint16) { 291 | dstSize := ra.data[offset] 292 | if dstSize == 0 { 293 | panic("Container size should NOT be zero") 294 | } 295 | 296 | // The src is a bitmapContainer. Just copy it over. 297 | if src[indexType] == typeBitmap { 298 | assert(src[indexSize] == maxContainerSize) 299 | bySize := uint16(maxContainerSize) - dstSize 300 | // Select the portion to the right of the container, beyond its right boundary. 301 | ra.scootRight(offset+uint64(dstSize), uint64(bySize)) 302 | ra.keys.updateOffsets(offset, uint64(bySize), true) 303 | assert(copy(ra.data[offset:], src) == len(src)) 304 | return 305 | } 306 | 307 | // src is an array container. Check if dstSize >= src. If so, just copy. 308 | // But, do keep dstSize intact, otherwise we'd lose portion of our container. 309 | if dstSize >= src[indexSize] { 310 | assert(copy(ra.data[offset:], src) == len(src)) 311 | ra.data[offset] = dstSize 312 | return 313 | } 314 | 315 | // dstSize < src. Determine the target size of the container. 316 | targetSz := stepSize(dstSize) 317 | for targetSz < src[indexSize] { 318 | targetSz = stepSize(targetSz) 319 | } 320 | 321 | if targetSz == maxContainerSize { 322 | // Looks like the targetSize is now maxSize. So, convert src to bitmap container. 323 | s := array(src) 324 | 325 | bySize := uint16(maxContainerSize) - dstSize 326 | // Select the portion to the right of the container, beyond its right boundary. 327 | ra.scootRight(offset+uint64(dstSize), uint64(bySize)) 328 | ra.keys.updateOffsets(offset, uint64(bySize), true) 329 | 330 | // Update the space of the container, so getContainer would work correctly. 331 | ra.data[offset] = maxContainerSize 332 | 333 | // Convert the src array to bitmap and write it directly over to the container. 334 | out := ra.getContainer(offset) 335 | Memclr(out) 336 | s.toBitmapContainer(out) 337 | return 338 | } 339 | 340 | // targetSize is not maxSize. Let's expand to targetSize and copy array. 341 | bySize := targetSz - dstSize 342 | ra.scootRight(offset+uint64(dstSize), uint64(bySize)) 343 | ra.keys.updateOffsets(offset, uint64(bySize), true) 344 | assert(copy(ra.data[offset:], src) == len(src)) 345 | ra.data[offset] = targetSz 346 | } 347 | 348 | func (ra Bitmap) getContainer(offset uint64) []uint16 { 349 | data := ra.data[offset:] 350 | if len(data) == 0 { 351 | panic(fmt.Sprintf("No container found at offset: %d\n", offset)) 352 | } 353 | sz := data[0] 354 | return data[:sz] 355 | } 356 | 357 | func (ra *Bitmap) Clone() *Bitmap { 358 | abuf := ra.ToBuffer() 359 | bbuf := make([]byte, len(abuf)) 360 | copy(bbuf, abuf) 361 | return FromBuffer(bbuf) 362 | } 363 | 364 | func (ra *Bitmap) IsEmpty() bool { 365 | if ra == nil { 366 | return true 367 | } 368 | N := ra.keys.numKeys() 369 | for i := 0; i < N; i++ { 370 | offset := ra.keys.val(i) 371 | cont := ra.getContainer(offset) 372 | if c := getCardinality(cont); c > 0 { 373 | return false 374 | } 375 | } 376 | return true 377 | } 378 | 379 | func (ra *Bitmap) Set(x uint64) bool { 380 | key := x & mask 381 | offset, has := ra.keys.getValue(key) 382 | if !has { 383 | // We need to add a container. 384 | o := ra.newContainer(minContainerSize) 385 | // offset might have been updated by setKey. 386 | offset = ra.setKey(key, o) 387 | } 388 | c := ra.getContainer(offset) 389 | switch c[indexType] { 390 | case typeArray: 391 | p := array(c) 392 | if added := p.add(uint16(x)); !added { 393 | return false 394 | } 395 | if p.isFull() { 396 | ra.expandContainer(offset) 397 | } 398 | return true 399 | case typeBitmap: 400 | b := bitmap(c) 401 | return b.add(uint16(x)) 402 | } 403 | panic("we shouldn't reach here") 404 | } 405 | 406 | func FromSortedList(vals []uint64) *Bitmap { 407 | var arr []uint16 408 | var hi, lastHi, off uint64 409 | 410 | ra := NewBitmap() 411 | 412 | if len(vals) == 0 { 413 | return ra 414 | } 415 | 416 | // Set the keys beforehand so that we don't need to move a lot of memory because of adding keys. 417 | var numKeys int 418 | for _, x := range vals { 419 | hi = x & mask 420 | if hi != 0 && hi != lastHi { 421 | numKeys++ 422 | } 423 | lastHi = hi 424 | } 425 | ra.initSpaceForKeys(numKeys) 426 | 427 | finalize := func(l []uint16, key uint64) { 428 | if len(l) == 0 { 429 | return 430 | } 431 | if len(l) <= 2048 { 432 | // 4 uint16s for the header, and extra 4 uint16s so that adding more elements using 433 | // Set operation doesn't fail. 434 | sz := uint16(8 + len(l)) 435 | off = ra.newContainer(sz) 436 | c := ra.getContainer(off) 437 | c[indexSize] = sz 438 | c[indexType] = typeArray 439 | setCardinality(c, len(l)) 440 | for i := 0; i < len(l); i++ { 441 | c[int(startIdx)+i] = l[i] 442 | } 443 | 444 | } else { 445 | off = ra.newContainer(maxContainerSize) 446 | c := ra.getContainer(off) 447 | c[indexSize] = maxContainerSize 448 | c[indexType] = typeBitmap 449 | for _, v := range l { 450 | bitmap(c).add(v) 451 | } 452 | } 453 | ra.setKey(key, off) 454 | return 455 | } 456 | 457 | lastHi = 0 458 | for _, x := range vals { 459 | hi = x & mask 460 | // Finalize the last container before proceeding ahead 461 | if hi != 0 && hi != lastHi { 462 | finalize(arr, lastHi) 463 | arr = arr[:0] 464 | } 465 | arr = append(arr, uint16(x)) 466 | lastHi = hi 467 | } 468 | finalize(arr, lastHi) 469 | return ra 470 | } 471 | 472 | // TODO: Potentially this can be optimized. 473 | func (ra *Bitmap) SetMany(vals []uint64) { 474 | for _, k := range vals { 475 | ra.Set(k) 476 | } 477 | } 478 | 479 | // Select returns the element at the xth index. (0-indexed) 480 | func (ra *Bitmap) Select(x uint64) (uint64, error) { 481 | if x >= uint64(ra.GetCardinality()) { 482 | return 0, errors.Errorf("index %d is not less than the cardinality: %d", 483 | x, ra.GetCardinality()) 484 | } 485 | n := ra.keys.numKeys() 486 | for i := 0; i < n; i++ { 487 | off := ra.keys.val(i) 488 | con := ra.getContainer(off) 489 | c := uint64(getCardinality(con)) 490 | assert(c != uint64(invalidCardinality)) 491 | if x < c { 492 | key := ra.keys.key(i) 493 | switch con[indexType] { 494 | case typeArray: 495 | return key | uint64(array(con).all()[x]), nil 496 | case typeBitmap: 497 | return key | uint64(bitmap(con).selectAt(int(x))), nil 498 | } 499 | } 500 | x -= c 501 | } 502 | panic("should not reach here") 503 | } 504 | 505 | func (ra *Bitmap) Contains(x uint64) bool { 506 | if ra == nil { 507 | return false 508 | } 509 | key := x & mask 510 | offset, has := ra.keys.getValue(key) 511 | if !has { 512 | return false 513 | } 514 | y := uint16(x) 515 | 516 | c := ra.getContainer(offset) 517 | switch c[indexType] { 518 | case typeArray: 519 | p := array(c) 520 | return p.has(y) 521 | case typeBitmap: 522 | b := bitmap(c) 523 | return b.has(y) 524 | } 525 | return false 526 | } 527 | 528 | func (ra *Bitmap) Remove(x uint64) bool { 529 | if ra == nil { 530 | return false 531 | } 532 | key := x & mask 533 | offset, has := ra.keys.getValue(key) 534 | if !has { 535 | return false 536 | } 537 | c := ra.getContainer(offset) 538 | switch c[indexType] { 539 | case typeArray: 540 | p := array(c) 541 | return p.remove(uint16(x)) 542 | case typeBitmap: 543 | b := bitmap(c) 544 | return b.remove(uint16(x)) 545 | } 546 | return true 547 | } 548 | 549 | // Remove range removes [lo, hi) from the bitmap. 550 | func (ra *Bitmap) RemoveRange(lo, hi uint64) { 551 | if lo > hi { 552 | panic("lo should not be more than hi") 553 | } 554 | if lo == hi { 555 | return 556 | } 557 | 558 | k1 := lo & mask 559 | k2 := hi & mask 560 | 561 | defer ra.Cleanup() 562 | 563 | // Complete range lie in a single container 564 | if k1 == k2 { 565 | if off, has := ra.keys.getValue(k1); has { 566 | c := ra.getContainer(off) 567 | removeRangeContainer(c, uint16(lo), uint16(hi)-1) 568 | } 569 | return 570 | } 571 | 572 | // Remove all the containers in range [k1+1, k2-1]. 573 | n := ra.keys.numKeys() 574 | st := ra.keys.search(k1) 575 | key := ra.keys.key(st) 576 | if key == k1 { 577 | st++ 578 | } 579 | 580 | for i := st; i < n; i++ { 581 | key := ra.keys.key(i) 582 | if key >= k2 { 583 | break 584 | } 585 | if off, has := ra.keys.getValue(key); has { 586 | zeroOutContainer(ra.getContainer(off)) 587 | } 588 | } 589 | 590 | // Remove elements >= lo in k1's container 591 | if off, has := ra.keys.getValue(k1); has { 592 | c := ra.getContainer(off) 593 | if uint16(lo) == 0 { 594 | zeroOutContainer(c) 595 | } else { 596 | removeRangeContainer(c, uint16(lo), math.MaxUint16) 597 | } 598 | } 599 | 600 | if uint16(hi) == 0 { 601 | return 602 | } 603 | 604 | // Remove all elements < hi in k2's container 605 | if off, has := ra.keys.getValue(k2); has { 606 | c := ra.getContainer(off) 607 | removeRangeContainer(c, 0, uint16(hi)-1) 608 | } 609 | } 610 | 611 | func (ra *Bitmap) Reset() { 612 | // reset ra.data to size enough for one container and corresponding key. 613 | // 2 u64 is needed for header and another 2 u16 for the key 0. 614 | ra.data = ra.data[:16+minContainerSize] 615 | ra.keys = toUint64Slice(ra.data) 616 | 617 | offset := ra.newContainer(minContainerSize) 618 | ra.keys.setAt(indexNodeStart+1, offset) 619 | ra.keys.setNumKeys(1) 620 | } 621 | 622 | func (ra *Bitmap) GetCardinality() int { 623 | if ra == nil { 624 | return 0 625 | } 626 | N := ra.keys.numKeys() 627 | var sz int 628 | for i := 0; i < N; i++ { 629 | offset := ra.keys.val(i) 630 | c := ra.getContainer(offset) 631 | sz += getCardinality(c) 632 | } 633 | return sz 634 | } 635 | 636 | func (ra *Bitmap) ToArray() []uint64 { 637 | if ra == nil { 638 | return nil 639 | } 640 | res := make([]uint64, 0, ra.GetCardinality()) 641 | N := ra.keys.numKeys() 642 | for i := 0; i < N; i++ { 643 | key := ra.keys.key(i) 644 | off := ra.keys.val(i) 645 | c := ra.getContainer(off) 646 | 647 | switch c[indexType] { 648 | case typeArray: 649 | a := array(c) 650 | for _, lo := range a.all() { 651 | res = append(res, key|uint64(lo)) 652 | } 653 | case typeBitmap: 654 | b := bitmap(c) 655 | out := b.all() 656 | for _, x := range out { 657 | res = append(res, key|uint64(x)) 658 | } 659 | } 660 | } 661 | return res 662 | } 663 | 664 | func (ra *Bitmap) String() string { 665 | var b strings.Builder 666 | b.WriteRune('\n') 667 | 668 | var usedSize, card int 669 | usedSize += 4 * (ra.keys.numKeys()) 670 | for i := 0; i < ra.keys.numKeys(); i++ { 671 | k := ra.keys.key(i) 672 | v := ra.keys.val(i) 673 | c := ra.getContainer(v) 674 | 675 | sz := c[indexSize] 676 | usedSize += int(sz) 677 | card += getCardinality(c) 678 | 679 | b.WriteString(fmt.Sprintf( 680 | "[%03d] Key: %#8x. Offset: %7d. Size: %4d. Type: %d. Card: %6d. Uint16/Uid: %.2f\n", 681 | i, k, v, sz, c[indexType], getCardinality(c), float64(sz)/float64(getCardinality(c)))) 682 | } 683 | b.WriteString(fmt.Sprintf("Number of containers: %d. Cardinality: %d\n", 684 | ra.keys.numKeys(), card)) 685 | 686 | amp := float64(len(ra.data)-usedSize) / float64(usedSize) 687 | b.WriteString(fmt.Sprintf( 688 | "Size in Uint16s. Used: %d. Total: %d. Space Amplification: %.2f%%. Moved: %.2fx\n", 689 | usedSize, len(ra.data), amp*100.0, float64(ra.memMoved)/float64(usedSize))) 690 | 691 | b.WriteString(fmt.Sprintf("Used Uint16/Uid: %.2f. Total Uint16/Uid: %.2f", 692 | float64(usedSize)/float64(card), float64(len(ra.data))/float64(card))) 693 | 694 | return b.String() 695 | } 696 | 697 | const fwd int = 0x01 698 | const rev int = 0x02 699 | 700 | func (ra *Bitmap) Minimum() uint64 { return ra.extreme(fwd) } 701 | func (ra *Bitmap) Maximum() uint64 { return ra.extreme(rev) } 702 | 703 | func (ra *Bitmap) Debug(x uint64) string { 704 | var b strings.Builder 705 | hi := x & mask 706 | off, found := ra.keys.getValue(hi) 707 | if !found { 708 | b.WriteString(fmt.Sprintf("Unable to find the container for x: %#x\n", hi)) 709 | b.WriteString(ra.String()) 710 | } 711 | c := ra.getContainer(off) 712 | lo := uint16(x) 713 | 714 | b.WriteString(fmt.Sprintf("x: %#x lo: %#x. offset: %d\n", x, lo, off)) 715 | 716 | switch c[indexType] { 717 | case typeArray: 718 | case typeBitmap: 719 | idx := lo / 16 720 | pos := lo % 16 721 | b.WriteString(fmt.Sprintf("At idx: %d. Pos: %d val: %#b\n", idx, pos, c[startIdx+idx])) 722 | } 723 | return b.String() 724 | } 725 | 726 | func (ra *Bitmap) extreme(dir int) uint64 { 727 | N := ra.keys.numKeys() 728 | if N == 0 { 729 | return 0 730 | } 731 | 732 | var k uint64 733 | var c []uint16 734 | 735 | if dir == fwd { 736 | for i := 0; i < N; i++ { 737 | offset := ra.keys.val(i) 738 | c = ra.getContainer(offset) 739 | if getCardinality(c) > 0 { 740 | k = ra.keys.key(i) 741 | break 742 | } 743 | } 744 | } else { 745 | for i := N - 1; i >= 0; i-- { 746 | offset := ra.keys.val(i) 747 | c = ra.getContainer(offset) 748 | if getCardinality(c) > 0 { 749 | k = ra.keys.key(i) 750 | break 751 | } 752 | } 753 | } 754 | 755 | switch c[indexType] { 756 | case typeArray: 757 | a := array(c) 758 | if dir == fwd { 759 | return k | uint64(a.minimum()) 760 | } 761 | return k | uint64(a.maximum()) 762 | case typeBitmap: 763 | b := bitmap(c) 764 | if dir == fwd { 765 | return k | uint64(b.minimum()) 766 | } 767 | return k | uint64(b.maximum()) 768 | default: 769 | panic("We don't support this type of container") 770 | } 771 | } 772 | 773 | func (ra *Bitmap) And(bm *Bitmap) { 774 | if bm == nil { 775 | ra.Reset() 776 | return 777 | } 778 | 779 | a, b := ra, bm 780 | ai, an := 0, a.keys.numKeys() 781 | bi, bn := 0, b.keys.numKeys() 782 | 783 | for ai < an && bi < bn { 784 | ak := a.keys.key(ai) 785 | bk := b.keys.key(bi) 786 | if ak == bk { 787 | off := a.keys.val(ai) 788 | ac := a.getContainer(off) 789 | 790 | off = b.keys.val(bi) 791 | bc := b.getContainer(off) 792 | 793 | // do the intersection 794 | // TODO: See if we can do containerAnd operation in-place. 795 | c := containerAnd(ac, bc) 796 | 797 | // create a new container and update the key offset to this container. 798 | offset := a.newContainer(uint16(len(c))) 799 | copy(a.data[offset:], c) 800 | a.setKey(ak, offset) 801 | ai++ 802 | bi++ 803 | } else if ak < bk { 804 | off := a.keys.val(ai) 805 | zeroOutContainer(a.getContainer(off)) 806 | ai++ 807 | } else { 808 | bi++ 809 | } 810 | } 811 | for ai < an { 812 | off := a.keys.val(ai) 813 | zeroOutContainer(a.getContainer(off)) 814 | ai++ 815 | } 816 | } 817 | 818 | func And(a, b *Bitmap) *Bitmap { 819 | ai, an := 0, a.keys.numKeys() 820 | bi, bn := 0, b.keys.numKeys() 821 | 822 | res := NewBitmap() 823 | for ai < an && bi < bn { 824 | ak := a.keys.key(ai) 825 | bk := a.keys.key(bi) 826 | if ak == bk { 827 | // Do the intersection. 828 | off := a.keys.val(ai) 829 | ac := a.getContainer(off) 830 | 831 | off = b.keys.val(bi) 832 | bc := b.getContainer(off) 833 | 834 | outc := containerAnd(ac, bc) 835 | if getCardinality(outc) > 0 { 836 | offset := res.newContainer(uint16(len(outc))) 837 | copy(res.data[offset:], outc) 838 | res.setKey(ak, offset) 839 | } 840 | ai++ 841 | bi++ 842 | } else if ak < bk { 843 | ai++ 844 | } else { 845 | bi++ 846 | } 847 | } 848 | return res 849 | } 850 | 851 | func (ra *Bitmap) AndNot(bm *Bitmap) { 852 | if bm == nil { 853 | return 854 | } 855 | a, b := ra, bm 856 | var ai, bi int 857 | 858 | buf := make([]uint16, maxContainerSize) 859 | for ai < a.keys.numKeys() && bi < b.keys.numKeys() { 860 | ak := a.keys.key(ai) 861 | bk := b.keys.key(bi) 862 | if ak == bk { 863 | off := a.keys.val(ai) 864 | ac := a.getContainer(off) 865 | 866 | off = b.keys.val(bi) 867 | bc := b.getContainer(off) 868 | 869 | // TODO: See if we can do containerAndNot operation in-place. 870 | c := containerAndNot(ac, bc, buf) 871 | // create a new container and update the key offset to this container. 872 | offset := a.newContainer(uint16(len(c))) 873 | copy(a.data[offset:], c) 874 | a.setKey(ak, offset) 875 | 876 | ai++ 877 | bi++ 878 | continue 879 | } 880 | if ak < bk { 881 | ai++ 882 | } else { 883 | bi++ 884 | } 885 | } 886 | } 887 | 888 | // TODO: Check if we want to use lazyMode 889 | func (dst *Bitmap) Or(src *Bitmap) { 890 | if src == nil { 891 | return 892 | } 893 | dst.or(src, runInline) 894 | } 895 | 896 | func (dst *Bitmap) or(src *Bitmap, runMode int) { 897 | srcIdx, numKeys := 0, src.keys.numKeys() 898 | 899 | buf := make([]uint16, maxContainerSize) 900 | for ; srcIdx < numKeys; srcIdx++ { 901 | srcCont := src.getContainer(src.keys.val(srcIdx)) 902 | if getCardinality(srcCont) == 0 { 903 | continue 904 | } 905 | 906 | key := src.keys.key(srcIdx) 907 | 908 | dstIdx := dst.keys.search(key) 909 | if dstIdx >= dst.keys.numKeys() || dst.keys.key(dstIdx) != key { 910 | // srcCont doesn't exist in dst. So, copy it over. 911 | offset := dst.newContainer(uint16(len(srcCont))) 912 | copy(dst.getContainer(offset), srcCont) 913 | dst.setKey(key, offset) 914 | } else { 915 | // Container exists in dst as well. Do an inline containerOr. 916 | offset := dst.keys.val(dstIdx) 917 | dstCont := dst.getContainer(offset) 918 | if c := containerOr(dstCont, srcCont, buf, runMode|runInline); len(c) > 0 { 919 | dst.copyAt(offset, c) 920 | dst.setKey(key, offset) 921 | } 922 | } 923 | } 924 | } 925 | 926 | func Or(a, b *Bitmap) *Bitmap { 927 | ai, an := 0, a.keys.numKeys() 928 | bi, bn := 0, b.keys.numKeys() 929 | 930 | buf := make([]uint16, maxContainerSize) 931 | res := NewBitmap() 932 | for ai < an && bi < bn { 933 | ak := a.keys.key(ai) 934 | ac := a.getContainer(a.keys.val(ai)) 935 | 936 | bk := b.keys.key(bi) 937 | bc := b.getContainer(b.keys.val(bi)) 938 | 939 | if ak == bk { 940 | // Do the union. 941 | outc := containerOr(ac, bc, buf, 0) 942 | offset := res.newContainer(uint16(len(outc))) 943 | copy(res.data[offset:], outc) 944 | res.setKey(ak, offset) 945 | ai++ 946 | bi++ 947 | } else if ak < bk { 948 | off := res.newContainer(uint16(len(ac))) 949 | copy(res.getContainer(off), ac) 950 | res.setKey(ak, off) 951 | ai++ 952 | } else { 953 | off := res.newContainer(uint16(len(bc))) 954 | copy(res.getContainer(off), bc) 955 | res.setKey(bk, off) 956 | bi++ 957 | } 958 | } 959 | for ai < an { 960 | ak := a.keys.key(ai) 961 | ac := a.getContainer(a.keys.val(ai)) 962 | off := res.newContainer(uint16(len(ac))) 963 | 964 | copy(res.getContainer(off), ac) 965 | res.setKey(ak, off) 966 | ai++ 967 | } 968 | for bi < bn { 969 | bk := b.keys.key(bi) 970 | bc := b.getContainer(b.keys.val(bi)) 971 | off := res.newContainer(uint16(len(bc))) 972 | 973 | copy(res.getContainer(off), bc) 974 | res.setKey(bk, off) 975 | bi++ 976 | } 977 | return res 978 | } 979 | 980 | func (ra *Bitmap) Rank(x uint64) int { 981 | key := x & mask 982 | offset, has := ra.keys.getValue(key) 983 | if !has { 984 | return -1 985 | } 986 | c := ra.getContainer(offset) 987 | y := uint16(x) 988 | 989 | // Find the rank within the container 990 | var rank int 991 | switch c[indexType] { 992 | case typeArray: 993 | rank = array(c).rank(y) 994 | case typeBitmap: 995 | rank = bitmap(c).rank(y) 996 | } 997 | if rank < 0 { 998 | return -1 999 | } 1000 | 1001 | // Add up cardinalities of all the containers on the left of container containing x. 1002 | n := ra.keys.numKeys() 1003 | for i := 0; i < n; i++ { 1004 | if ra.keys.key(i) == key { 1005 | break 1006 | } 1007 | cont := ra.getContainer(ra.keys.val(i)) 1008 | rank += getCardinality(cont) 1009 | } 1010 | return rank 1011 | } 1012 | 1013 | func (ra *Bitmap) Cleanup() { 1014 | type interval struct { 1015 | start uint64 1016 | end uint64 1017 | } 1018 | 1019 | // Find the ranges that needs to be removed in the key space and the container space. Also, 1020 | // start the iteration from idx = 1 because we never remove the 0 key. 1021 | var keyIntervals, contIntervals []interval 1022 | for idx := 1; idx < ra.keys.numKeys(); idx++ { 1023 | off := ra.keys.val(idx) 1024 | cont := ra.getContainer(off) 1025 | if getCardinality(cont) == 0 { 1026 | ko := uint64(keyOffset(idx)) 1027 | contIntervals = append(contIntervals, interval{off, off + uint64(cont[indexSize])}) 1028 | keyIntervals = append(keyIntervals, interval{4 * ko, 4 * (ko + 2)}) 1029 | } 1030 | } 1031 | if len(contIntervals) == 0 { 1032 | return 1033 | } 1034 | 1035 | merge := func(intervals []interval) []interval { 1036 | assert(len(intervals) > 0) 1037 | 1038 | // Merge the ranges in order to reduce scootLeft 1039 | merged := []interval{intervals[0]} 1040 | for _, ir := range intervals[1:] { 1041 | last := merged[len(merged)-1] 1042 | if ir.start == last.end { 1043 | last.end = ir.end 1044 | merged[len(merged)-1] = last 1045 | continue 1046 | } 1047 | merged = append(merged, ir) 1048 | } 1049 | return merged 1050 | } 1051 | 1052 | // Key intervals are already sorted, but container intervals needs to be sorted because 1053 | // they are always added in the end of the ra.data. 1054 | sort.Slice(contIntervals, func(i, j int) bool { 1055 | return contIntervals[i].start < contIntervals[j].start 1056 | }) 1057 | 1058 | contIntervals = merge(contIntervals) 1059 | keyIntervals = merge(keyIntervals) 1060 | 1061 | // Cleanup the containers. 1062 | moved := uint64(0) 1063 | for _, ir := range contIntervals { 1064 | assert(ir.start >= moved) 1065 | sz := ir.end - ir.start 1066 | ra.scootLeft(ir.start-moved, sz) 1067 | ra.keys.updateOffsets(ir.end-moved-1, sz, false) 1068 | moved += sz 1069 | } 1070 | 1071 | // Cleanup the key space. 1072 | moved = uint64(0) 1073 | for _, ir := range keyIntervals { 1074 | assert(ir.start >= moved) 1075 | sz := ir.end - ir.start 1076 | ra.scootLeft(ir.start-moved, sz) 1077 | 1078 | // sz is in number of u16s, hence number of key-value removed is sz/8. 1079 | ra.keys.setNumKeys(ra.keys.numKeys() - int(sz/8)) 1080 | ra.keys.setNodeSize(ra.keys.size() - int(sz)) 1081 | ra.keys = ra.keys[:len(ra.keys)-int(sz/4)] 1082 | ra.keys.updateOffsets(ir.end-moved-1, sz, false) 1083 | moved += sz 1084 | } 1085 | } 1086 | 1087 | func FastAnd(bitmaps ...*Bitmap) *Bitmap { 1088 | if len(bitmaps) == 0 { 1089 | return NewBitmap() 1090 | } 1091 | b := bitmaps[0] 1092 | for _, bm := range bitmaps[1:] { 1093 | b.And(bm) 1094 | } 1095 | b.Cleanup() 1096 | return b 1097 | } 1098 | 1099 | // FastParOr would group up bitmaps and call FastOr on them concurrently. It 1100 | // would then merge the groups into final Bitmap. This approach is simpler and 1101 | // faster than operating at a container level, because we can't operate on array 1102 | // containers belonging to the same Bitmap concurrently because array containers 1103 | // can expand, leaving no clear boundaries. 1104 | // 1105 | // If FastParOr is called with numGo=1, it just calls FastOr. 1106 | // 1107 | // Experiments with numGo=4 shows that FastParOr would be 2x the speed of 1108 | // FastOr, but 4x the memory usage, even under 50% CPU usage. So, use wisely. 1109 | func FastParOr(numGo int, bitmaps ...*Bitmap) *Bitmap { 1110 | if numGo == 1 { 1111 | return FastOr(bitmaps...) 1112 | } 1113 | width := max(len(bitmaps)/numGo, 3) 1114 | 1115 | var wg sync.WaitGroup 1116 | var res []*Bitmap 1117 | for start := 0; start < len(bitmaps); start += width { 1118 | end := min(start+width, len(bitmaps)) 1119 | res = append(res, nil) // Make space for result. 1120 | wg.Add(1) 1121 | 1122 | go func(start, end int) { 1123 | idx := start / width 1124 | res[idx] = FastOr(bitmaps[start:end]...) 1125 | wg.Done() 1126 | }(start, end) 1127 | } 1128 | wg.Wait() 1129 | return FastOr(res...) 1130 | } 1131 | 1132 | // FastOr would merge given Bitmaps into one Bitmap. This is faster than 1133 | // doing an OR over the bitmaps iteratively. 1134 | func FastOr(bitmaps ...*Bitmap) *Bitmap { 1135 | if len(bitmaps) == 0 { 1136 | return NewBitmap() 1137 | } 1138 | if len(bitmaps) == 1 { 1139 | return bitmaps[0] 1140 | } 1141 | 1142 | // We first figure out the container distribution across the bitmaps. We do 1143 | // that by looking at the key of the container, and the cardinality. We 1144 | // assume the worst-case scenario where the union would result in a 1145 | // cardinality (per container) of the sum of cardinalities of each of the 1146 | // corresponding containers in other bitmaps. 1147 | containers := make(map[uint64]int) 1148 | for _, b := range bitmaps { 1149 | for i := 0; i < b.keys.numKeys(); i++ { 1150 | offset := b.keys.val(i) 1151 | cont := b.getContainer(offset) 1152 | card := getCardinality(cont) 1153 | containers[b.keys.key(i)] += card 1154 | } 1155 | } 1156 | 1157 | // We use the above information to pre-generate the destination Bitmap and 1158 | // allocate container sizes based on the calculated cardinalities. 1159 | // var sz int 1160 | dst := NewBitmap() 1161 | // First create the keys. We do this as a separate step, because keys are 1162 | // the left most portion of the data array. Adding space there requires 1163 | // moving a lot of pieces. 1164 | for key, card := range containers { 1165 | if card > 0 { 1166 | dst.setKey(key, 0) 1167 | } 1168 | } 1169 | 1170 | // Then create the bitmap containers. 1171 | for key, card := range containers { 1172 | if card >= 4096 { 1173 | offset := dst.newContainer(maxContainerSize) 1174 | c := dst.getContainer(offset) 1175 | c[indexSize] = maxContainerSize 1176 | c[indexType] = typeBitmap 1177 | dst.setKey(key, offset) 1178 | } 1179 | } 1180 | 1181 | // Create the array containers at the end. This allows them to expand 1182 | // without having to move a lot of memory. 1183 | for key, card := range containers { 1184 | // Ensure this condition exactly maps up with above. 1185 | if card < 4096 && card > 0 { 1186 | if card < minContainerSize { 1187 | card = minContainerSize 1188 | } 1189 | offset := dst.newContainer(uint16(card)) 1190 | c := dst.getContainer(offset) 1191 | c[indexSize] = uint16(card) 1192 | c[indexType] = typeArray 1193 | dst.setKey(key, offset) 1194 | } 1195 | } 1196 | 1197 | // dst Bitmap is ready to be ORed with the given Bitmaps. 1198 | for _, b := range bitmaps { 1199 | dst.or(b, runLazy) 1200 | } 1201 | 1202 | for i := 0; i < dst.keys.numKeys(); i++ { 1203 | offset := dst.keys.val(i) 1204 | c := dst.getContainer(offset) 1205 | if getCardinality(c) == invalidCardinality { 1206 | calculateAndSetCardinality(c) 1207 | } 1208 | } 1209 | 1210 | return dst 1211 | } 1212 | 1213 | // Split splits the bitmap based on maxSz and the externalSize function. It splits the bitmap 1214 | // such that size of each split bitmap + external size corresponding to its elements approximately 1215 | // equal to maxSz (it can be greater than maxSz sometimes). The splits are returned in sorted order. 1216 | // externalSize is a function that should return the external size corresponding to elements in 1217 | // range [start, end]. External size is used to calculate the split boundaries. 1218 | func (bm *Bitmap) Split(externalSize func(start, end uint64) uint64, maxSz uint64) []*Bitmap { 1219 | splitFurther := func(b *Bitmap) []*Bitmap { 1220 | itr := b.NewIterator() 1221 | newBm := NewBitmap() 1222 | var sz uint64 1223 | var bms []*Bitmap 1224 | for id := itr.Next(); id != 0; id = itr.Next() { 1225 | sz += externalSize(id, id) 1226 | newBm.Set(id) 1227 | if sz >= maxSz { 1228 | bms = append(bms, newBm) 1229 | newBm = NewBitmap() 1230 | sz = 0 1231 | } 1232 | } 1233 | 1234 | if !newBm.IsEmpty() { 1235 | bms = append(bms, newBm) 1236 | } 1237 | return bms 1238 | } 1239 | 1240 | create := func(keyToOffset map[uint64]uint64, totalSz uint64) []*Bitmap { 1241 | var keys []uint64 1242 | for key := range keyToOffset { 1243 | keys = append(keys, key) 1244 | } 1245 | sort.Slice(keys, func(i, j int) bool { 1246 | return keys[i] < keys[j] 1247 | }) 1248 | 1249 | newBm := NewBitmap() 1250 | 1251 | // First set all the keys. 1252 | var containerSz uint64 1253 | for _, key := range keys { 1254 | newBm.setKey(key, 0) 1255 | 1256 | // Calculate the size of the containers. 1257 | cont := bm.getContainer(keyToOffset[key]) 1258 | containerSz += uint64(len(cont)) 1259 | } 1260 | // Allocate enough space to hold all the containers. 1261 | beforeSize := len(newBm.data) 1262 | newBm.fastExpand(containerSz) 1263 | newBm.data = newBm.data[:beforeSize] 1264 | 1265 | // Now, we can populate the containers. For that, we first expand the 1266 | // bitmap. Calculate the total size we need to allocate all these containers. 1267 | for _, key := range keys { 1268 | cont := bm.getContainer(keyToOffset[key]) 1269 | off := newBm.newContainer(uint16(len(cont))) 1270 | copy(newBm.data[off:], cont) 1271 | 1272 | newBm.setKey(key, off) 1273 | } 1274 | 1275 | if newBm.GetCardinality() == 0 { 1276 | return nil 1277 | } 1278 | 1279 | if totalSz > maxSz { 1280 | return splitFurther(newBm) 1281 | } 1282 | 1283 | return []*Bitmap{newBm} 1284 | } 1285 | 1286 | var splits []*Bitmap 1287 | 1288 | containerMap := make(map[uint64]uint64) 1289 | var totalSz uint64 // size of containers plus the external size of the container 1290 | 1291 | for i := 0; i < bm.keys.numKeys(); i++ { 1292 | key := bm.keys.key(i) 1293 | off := bm.keys.val(i) 1294 | cont := bm.getContainer(off) 1295 | 1296 | start, end := key, addUint64(key, 1<<16-1) 1297 | sz := externalSize(start, end) + 2*uint64(cont[indexSize]) // Converting to bytes. 1298 | 1299 | // We can probably append more containers in the same bucket. 1300 | if totalSz+sz < maxSz || len(containerMap) == 0 { 1301 | // Include this container in the container map. 1302 | containerMap[key] = off 1303 | totalSz += sz 1304 | continue 1305 | } 1306 | 1307 | // We have reached the maxSz limit. Hence, create a split. 1308 | splits = append(splits, create(containerMap, totalSz)...) 1309 | 1310 | containerMap = make(map[uint64]uint64) 1311 | containerMap[key] = off 1312 | totalSz = sz 1313 | } 1314 | if len(containerMap) > 0 { 1315 | splits = append(splits, create(containerMap, totalSz)...) 1316 | } 1317 | 1318 | return splits 1319 | } 1320 | -------------------------------------------------------------------------------- /bitmap_test.go: -------------------------------------------------------------------------------- 1 | package sroar 2 | 3 | import ( 4 | "math" 5 | "math/rand" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func fill(c []uint16, b uint16) { 13 | for i := range c[startIdx:] { 14 | c[i+int(startIdx)] = b 15 | } 16 | } 17 | 18 | func TestModify(t *testing.T) { 19 | data := make([]uint16, 16) 20 | s := toUint64Slice(data) 21 | for i := 0; i < len(s); i++ { 22 | s[i] = uint64(i) 23 | } 24 | 25 | o := toUint64Slice(data) 26 | for i := 0; i < len(o); i++ { 27 | require.Equal(t, uint64(i), o[i]) 28 | } 29 | } 30 | 31 | func TestContainer(t *testing.T) { 32 | ra := NewBitmap() 33 | 34 | // We're creating a container of size 64 words. 4 of these would be used for 35 | // the header. So, the data can only live in 60 words. 36 | offset := ra.newContainer(64) 37 | c := ra.getContainer(offset) 38 | require.Equal(t, uint16(64), ra.data[offset]) 39 | require.Equal(t, uint16(0), c[indexCardinality]) 40 | 41 | fill(c, 0xFF) 42 | for i, u := range c[startIdx:] { 43 | if i < 60 { 44 | require.Equalf(t, uint16(0xFF), u, "at index: %d", i) 45 | } else { 46 | require.Equalf(t, uint16(0x00), u, "at index: %d", i) 47 | } 48 | } 49 | 50 | offset2 := ra.newContainer(32) // Add a second container. 51 | c2 := ra.getContainer(offset2) 52 | require.Equal(t, uint16(32), ra.data[offset2]) 53 | fill(c2, 0xEE) 54 | 55 | // Expand the first container. This would push out the second container, so update its offset. 56 | ra.expandContainer(offset) 57 | offset2 += 64 58 | 59 | // Check if the second container is correct. 60 | c2 = ra.getContainer(offset2) 61 | require.Equal(t, uint16(32), ra.data[offset2]) 62 | require.Equal(t, 32, len(c2)) 63 | for _, val := range c2[startIdx:] { 64 | require.Equal(t, uint16(0xEE), val) 65 | } 66 | 67 | // Check if the first container is correct. 68 | c = ra.getContainer(offset) 69 | require.Equal(t, uint16(128), ra.data[offset]) 70 | require.Equal(t, 128, len(c)) 71 | for i, u := range c[startIdx:] { 72 | if i < 60 { 73 | require.Equalf(t, uint16(0xFF), u, "at index: %d", i) 74 | } else { 75 | require.Equalf(t, uint16(0x00), u, "at index: %d", i) 76 | } 77 | } 78 | } 79 | 80 | func TestKey(t *testing.T) { 81 | ra := NewBitmap() 82 | for i := 1; i <= 10; i++ { 83 | ra.Set(uint64(i)) 84 | } 85 | 86 | off, has := ra.keys.getValue(0) 87 | require.True(t, has) 88 | c := ra.getContainer(off) 89 | require.Equal(t, uint16(10), c[indexCardinality]) 90 | 91 | // Create 10 containers 92 | for i := 0; i < 10; i++ { 93 | t.Logf("Creating a new container: %d\n", i) 94 | ra.Set(uint64(i)<<16 + 1) 95 | } 96 | 97 | for i := 0; i < 10; i++ { 98 | ra.Set(uint64(i)<<16 + 2) 99 | } 100 | 101 | for i := 1; i < 10; i++ { 102 | offset, has := ra.keys.getValue(uint64(i) << 16) 103 | require.True(t, has) 104 | c = ra.getContainer(offset) 105 | require.Equal(t, uint16(2), c[indexCardinality]) 106 | } 107 | 108 | // Do add in the reverse order. 109 | for i := 19; i >= 10; i-- { 110 | ra.Set(uint64(i)<<16 + 2) 111 | } 112 | 113 | for i := 10; i < 20; i++ { 114 | offset, has := ra.keys.getValue(uint64(i) << 16) 115 | require.True(t, has) 116 | c = ra.getContainer(offset) 117 | require.Equal(t, uint16(1), c[indexCardinality]) 118 | } 119 | } 120 | 121 | func TestEdgeCase(t *testing.T) { 122 | ra := NewBitmap() 123 | 124 | require.True(t, ra.Set(65536)) 125 | require.True(t, ra.Contains(65536)) 126 | } 127 | 128 | func TestBulkAdd(t *testing.T) { 129 | ra := NewBitmap() 130 | m := make(map[uint64]struct{}) 131 | max := int64(64 << 16) 132 | start := time.Now() 133 | 134 | var cnt int 135 | for i := 0; ; i++ { 136 | if i%100 == 0 && time.Since(start) > time.Second { 137 | cnt++ 138 | start = time.Now() 139 | // t.Logf("Bitmap:\n%s\n", ra) 140 | if cnt == 3 { 141 | t.Logf("Breaking out of the loop\n") 142 | break 143 | } 144 | } 145 | x := uint64(rand.Int63n(max)) 146 | 147 | if _, has := m[x]; has { 148 | if !ra.Contains(x) { 149 | t.Logf("x should be present: %d %#x. Bitmap: %s\n", x, x, ra) 150 | off, found := ra.keys.getValue(x & mask) 151 | assert(found) 152 | c := ra.getContainer(off) 153 | lo := uint16(x) 154 | t.Logf("x: %#x lo: %#x. offset: %d\n", x, lo, off) 155 | switch c[indexType] { 156 | case typeArray: 157 | case typeBitmap: 158 | idx := lo / 16 159 | pos := lo % 16 160 | t.Logf("At idx: %d. Pos: %d val: %#b\n", idx, pos, c[startIdx+idx]) 161 | } 162 | 163 | t.Logf("Added: %d %#x. Added: %v\n", x, x, ra.Set(x)) 164 | t.Logf("After add. has: %v\n", ra.Contains(x)) 165 | 166 | // t.Logf("Hex dump of container at offset: %d\n%s\n", off, hex.Dump(toByteSlice(c))) 167 | t.FailNow() 168 | } 169 | continue 170 | } 171 | m[x] = struct{}{} 172 | // fmt.Printf("Setting x: %#x\n", x) 173 | if added := ra.Set(x); !added { 174 | t.Logf("Unable to set: %d %#x\n", x, x) 175 | t.Logf("ra.Has(x): %v\n", ra.Contains(x)) 176 | t.FailNow() 177 | } 178 | // for x := range m { 179 | // if !ra.Has(x) { 180 | // t.Logf("has(x) failed: %#x\n", x) 181 | // t.Logf("Debug: %s\n", ra.Debug(x)) 182 | // t.FailNow() 183 | // } 184 | // } 185 | // require.Truef(t, ra.Set(x), "Unable to set x: %d %#x\n", x, x) 186 | } 187 | t.Logf("Card: %d\n", len(m)) 188 | require.Equalf(t, len(m), ra.GetCardinality(), "Bitmap:\n%s\n", ra) 189 | for x := range m { 190 | require.True(t, ra.Contains(x)) 191 | } 192 | 193 | // _, has := ra.keys.getValue(0) 194 | // require.True(t, has) 195 | // for i := uint64(1); i <= max; i++ { 196 | // require.Truef(t, ra.Has(i), "i=%d", i) 197 | // } 198 | // t.Logf("Data size: %d\n", len(ra.data)) 199 | 200 | t.Logf("Copying data. Size: %d\n", len(ra.data)) 201 | dup := make([]uint16, len(ra.data)) 202 | copy(dup, ra.data) 203 | 204 | ra2 := FromBuffer(toByteSlice(dup)) 205 | require.Equal(t, len(m), ra2.GetCardinality()) 206 | for x := range m { 207 | require.True(t, ra2.Contains(x)) 208 | } 209 | } 210 | 211 | func TestBitmapUint64Max(t *testing.T) { 212 | bm := NewBitmap() 213 | 214 | edges := []uint64{0, math.MaxUint8, math.MaxUint16, math.MaxUint32, math.MaxUint64} 215 | for _, e := range edges { 216 | bm.Set(e) 217 | } 218 | for _, e := range edges { 219 | require.True(t, bm.Contains(e)) 220 | } 221 | } 222 | 223 | func TestBitmapZero(t *testing.T) { 224 | bm1 := NewBitmap() 225 | bm1.Set(1) 226 | uids := bm1.ToArray() 227 | require.Equal(t, 1, len(uids)) 228 | for _, u := range uids { 229 | require.Equal(t, uint64(1), u) 230 | } 231 | 232 | bm2 := NewBitmap() 233 | bm2.Set(2) 234 | 235 | bm3 := Or(bm1, bm2) 236 | require.False(t, bm3.Contains(0)) 237 | require.True(t, bm3.Contains(1)) 238 | require.True(t, bm3.Contains(2)) 239 | require.Equal(t, 2, bm3.GetCardinality()) 240 | } 241 | 242 | func TestBitmapOps(t *testing.T) { 243 | M := int64(10000) 244 | // smaller bitmap would always operate with [0, M) range. 245 | // max for each bitmap = M * F 246 | F := []int64{1, 10, 100, 1000} 247 | N := 10000 248 | 249 | for _, f := range F { 250 | t.Logf("Using N: %d M: %d F: %d\n", N, M, f) 251 | small, big := NewBitmap(), NewBitmap() 252 | occ := make(map[uint64]int) 253 | smallMap := make(map[uint64]struct{}) 254 | bigMap := make(map[uint64]struct{}) 255 | 256 | for i := 0; i < N; i++ { 257 | smallx := uint64(rand.Int63n(M)) 258 | 259 | _, has := smallMap[smallx] 260 | added := small.Set(smallx) 261 | if has { 262 | require.False(t, added, "Can't readd already present x: %d", smallx) 263 | } 264 | smallMap[smallx] = struct{}{} 265 | 266 | bigx := uint64(rand.Int63n(M * f)) 267 | _, has = bigMap[bigx] 268 | added = big.Set(bigx) 269 | if has { 270 | require.False(t, added, "Can't readd already present x: %d", bigx) 271 | } 272 | bigMap[bigx] = struct{}{} 273 | 274 | occ[smallx] |= 0x01 // binary 0001 275 | occ[bigx] |= 0x02 // binary 0010 276 | } 277 | require.Equal(t, len(smallMap), small.GetCardinality()) 278 | require.Equal(t, len(bigMap), big.GetCardinality()) 279 | 280 | bitOr := Or(small, big) 281 | bitAnd := And(small, big) 282 | 283 | t.Logf("Sizes. small: %d big: %d, bitOr: %d bitAnd: %d\n", 284 | small.GetCardinality(), big.GetCardinality(), 285 | bitOr.GetCardinality(), bitAnd.GetCardinality()) 286 | 287 | cntOr, cntAnd := 0, 0 288 | for x, freq := range occ { 289 | if freq == 0x00 { 290 | require.Failf(t, "Failed", "Value of freq can't be zero. Found: %#x\n", freq) 291 | } else if freq == 0x01 { 292 | _, has := smallMap[x] 293 | require.True(t, has) 294 | require.True(t, small.Contains(x)) 295 | require.Truef(t, bitOr.Contains(x), "Expected %d %#x. But, not found. freq: %#x\n", 296 | x, x, freq) 297 | cntOr++ 298 | 299 | } else if freq == 0x02 { 300 | // one of them has it. 301 | _, has := bigMap[x] 302 | require.True(t, has) 303 | require.True(t, big.Contains(x)) 304 | require.Truef(t, bitOr.Contains(x), "Expected %d %#x. But, not found. freq: %#x\n", 305 | x, x, freq) 306 | cntOr++ 307 | 308 | } else if freq == 0x03 { 309 | require.True(t, small.Contains(x)) 310 | require.True(t, big.Contains(x)) 311 | require.Truef(t, bitAnd.Contains(x), "x: %#x\n", x) 312 | cntOr++ 313 | cntAnd++ 314 | } else { 315 | require.Failf(t, "Failed", "Value of freq can't exceed 0x03. Found: %#x\n", freq) 316 | } 317 | } 318 | if cntAnd != bitAnd.GetCardinality() { 319 | uids := bitAnd.ToArray() 320 | t.Logf("Len Uids: %d Card: %d cntAnd: %d. Occ: %d\n", len(uids), bitAnd.GetCardinality(), cntAnd, len(occ)) 321 | 322 | uidMap := make(map[uint64]struct{}) 323 | for _, u := range uids { 324 | uidMap[u] = struct{}{} 325 | } 326 | for u := range occ { 327 | delete(uidMap, u) 328 | } 329 | for x := range uidMap { 330 | t.Logf("Remaining uids in UidMap: %d %#b\n", x, x) 331 | } 332 | require.FailNow(t, "Cardinality isn't matching") 333 | } 334 | require.Equal(t, cntOr, bitOr.GetCardinality()) 335 | require.Equal(t, cntAnd, bitAnd.GetCardinality()) 336 | } 337 | } 338 | 339 | func TestUint16(t *testing.T) { 340 | a := uint16(0xfeff) 341 | b := uint16(0x100) 342 | t.Logf("a & b: %#x", a&b) 343 | var x uint16 344 | for i := 0; i < 100000; i++ { 345 | prev := x 346 | x++ 347 | if x <= prev { 348 | // This triggers when prev = 0xFFFF. 349 | // require.Failf(t, "x<=prev", "x %d <= prev %d", x, prev) 350 | } 351 | } 352 | } 353 | 354 | func TestSetGet(t *testing.T) { 355 | bm := NewBitmap() 356 | N := int(1e6) 357 | for i := 0; i < N; i++ { 358 | bm.Set(uint64(i)) 359 | } 360 | for i := 0; i < N; i++ { 361 | has := bm.Contains(uint64(i)) 362 | require.True(t, has) 363 | } 364 | } 365 | 366 | func TestSetSorted(t *testing.T) { 367 | check := func(n int) { 368 | var arr []uint64 369 | for i := 0; i < n; i++ { 370 | arr = append(arr, uint64(i)) 371 | } 372 | r := FromSortedList(arr) 373 | require.Equal(t, len(arr), r.GetCardinality()) 374 | 375 | rarr := r.ToArray() 376 | for i := 0; i < n; i++ { 377 | require.Equal(t, uint64(i), rarr[i]) 378 | } 379 | 380 | r.Set(uint64(n)) 381 | require.True(t, r.Contains(uint64(n))) 382 | } 383 | check(10) 384 | check(1e6) 385 | } 386 | 387 | func TestAnd(t *testing.T) { 388 | a := NewBitmap() 389 | b := NewBitmap() 390 | 391 | N := int(1e7) 392 | for i := 0; i < N; i++ { 393 | if i%2 == 0 { 394 | a.Set(uint64(i)) 395 | } else { 396 | b.Set(uint64(i)) 397 | } 398 | } 399 | require.Equal(t, N/2, a.GetCardinality()) 400 | require.Equal(t, N/2, b.GetCardinality()) 401 | res := And(a, b) 402 | require.Equal(t, 0, res.GetCardinality()) 403 | a.And(b) 404 | require.Equal(t, 0, a.GetCardinality()) 405 | } 406 | 407 | func TestAnd2(t *testing.T) { 408 | a := NewBitmap() 409 | n := int(1e7) 410 | 411 | for i := 0; i < n; i++ { 412 | a.Set(uint64(i)) 413 | } 414 | require.Equal(t, n, a.GetCardinality()) 415 | a.RemoveRange(0, uint64(n/2)) 416 | 417 | for i := 0; i < n; i++ { 418 | a.Set(uint64(i)) 419 | } 420 | require.Equal(t, n, a.GetCardinality()) 421 | } 422 | 423 | func TestAndNot(t *testing.T) { 424 | a := NewBitmap() 425 | b := NewBitmap() 426 | 427 | N := int(1e7) 428 | for i := 0; i < N; i++ { 429 | a.Set(uint64(i)) 430 | if i < N/2 { 431 | b.Set(uint64(i)) 432 | } 433 | } 434 | require.Equal(t, N, a.GetCardinality()) 435 | require.Equal(t, N/2, b.GetCardinality()) 436 | 437 | a.AndNot(b) 438 | require.Equal(t, N/2, a.GetCardinality()) 439 | 440 | // Test for case when array container will be generated. 441 | a = NewBitmap() 442 | b = NewBitmap() 443 | 444 | a.SetMany([]uint64{1, 2, 3, 4}) 445 | b.SetMany([]uint64{3, 4, 5, 6}) 446 | 447 | a.AndNot(b) 448 | require.Equal(t, []uint64{1, 2}, a.ToArray()) 449 | 450 | // Test for case when bitmap container will be generated. 451 | a = NewBitmap() 452 | b = NewBitmap() 453 | for i := 0; i < 10000; i++ { 454 | a.Set(uint64(i)) 455 | if i < 7000 { 456 | b.Set(uint64(i)) 457 | } 458 | } 459 | a.AndNot(b) 460 | require.Equal(t, 3000, a.GetCardinality()) 461 | for i := 0; i < 10000; i++ { 462 | if i < 7000 { 463 | require.False(t, a.Contains(uint64(i))) 464 | } else { 465 | require.True(t, a.Contains(uint64(i))) 466 | } 467 | } 468 | } 469 | 470 | func TestAndNot2(t *testing.T) { 471 | a := NewBitmap() 472 | b := NewBitmap() 473 | n := int(1e6) 474 | 475 | for i := 0; i < n/2; i++ { 476 | a.Set(uint64(i)) 477 | } 478 | for i := n / 2; i < n; i++ { 479 | b.Set(uint64(i)) 480 | } 481 | require.Equal(t, n/2, a.GetCardinality()) 482 | a.AndNot(b) 483 | require.Equal(t, n/2, a.GetCardinality()) 484 | 485 | } 486 | 487 | func TestOr(t *testing.T) { 488 | a := NewBitmap() 489 | b := NewBitmap() 490 | 491 | N := int(1e7) 492 | for i := 0; i < N; i++ { 493 | if i%2 == 0 { 494 | a.Set(uint64(i)) 495 | } else { 496 | b.Set(uint64(i)) 497 | } 498 | } 499 | require.Equal(t, N/2, a.GetCardinality()) 500 | require.Equal(t, N/2, b.GetCardinality()) 501 | res := Or(a, b) 502 | require.Equal(t, N, res.GetCardinality()) 503 | a.or(b, 0) 504 | require.Equal(t, N, a.GetCardinality()) 505 | } 506 | 507 | func TestCardinality(t *testing.T) { 508 | a := NewBitmap() 509 | n := 1 << 20 510 | for i := 0; i < n; i++ { 511 | a.Set(uint64(i)) 512 | } 513 | require.Equal(t, n, a.GetCardinality()) 514 | } 515 | 516 | func TestRemove(t *testing.T) { 517 | a := NewBitmap() 518 | N := int(1e7) 519 | for i := 0; i < N; i++ { 520 | a.Set(uint64(i)) 521 | } 522 | require.Equal(t, N, a.GetCardinality()) 523 | for i := 0; i < N/2; i++ { 524 | require.True(t, a.Remove(uint64(i))) 525 | } 526 | require.Equal(t, N/2, a.GetCardinality()) 527 | 528 | // Remove elelemts which doesn't exist should be no-op 529 | for i := 0; i < N/2; i++ { 530 | require.False(t, a.Remove(uint64(i))) 531 | } 532 | require.Equal(t, N/2, a.GetCardinality()) 533 | 534 | for i := 0; i < N/2; i++ { 535 | require.True(t, a.Remove(uint64(i+N/2))) 536 | } 537 | require.Equal(t, 0, a.GetCardinality()) 538 | } 539 | 540 | func TestContainerRemoveRange(t *testing.T) { 541 | ra := NewBitmap() 542 | 543 | type cases struct { 544 | lo uint16 545 | hi uint16 546 | expected []uint16 547 | } 548 | 549 | testBitmap := func(tc cases) { 550 | offset := ra.newContainer(maxContainerSize) 551 | c := ra.getContainer(offset) 552 | c[indexType] = typeBitmap 553 | a := bitmap(c) 554 | 555 | for i := 1; i <= 5; i++ { 556 | a.add(uint16(5 * i)) 557 | } 558 | a.removeRange(tc.lo, tc.hi) 559 | result := a.all() 560 | require.Equalf(t, len(tc.expected), getCardinality(a), "case: %+v, actual:%v\n", tc, result) 561 | require.Equalf(t, tc.expected, result, "case: %+v actual: %v\n", tc, result) 562 | } 563 | 564 | testArray := func(tc cases) { 565 | offset := ra.newContainer(maxContainerSize) 566 | c := ra.getContainer(offset) 567 | c[indexType] = typeArray 568 | a := array(c) 569 | 570 | for i := 1; i <= 5; i++ { 571 | a.add(uint16(5 * i)) 572 | } 573 | a.removeRange(tc.lo, tc.hi) 574 | result := a.all() 575 | require.Equalf(t, len(tc.expected), getCardinality(a), "case: %+v, actual:%v\n", tc, result) 576 | require.Equalf(t, tc.expected, result, "case: %+v actual: %v\n", tc, result) 577 | } 578 | 579 | tests := []cases{ 580 | {8, 22, []uint16{5, 25}}, 581 | {8, 20, []uint16{5, 25}}, 582 | {10, 22, []uint16{5, 25}}, 583 | {10, 20, []uint16{5, 25}}, 584 | {7, 11, []uint16{5, 15, 20, 25}}, 585 | {7, 10, []uint16{5, 15, 20, 25}}, 586 | {10, 11, []uint16{5, 15, 20, 25}}, 587 | {0, 0, []uint16{5, 10, 15, 20, 25}}, 588 | {30, 30, []uint16{5, 10, 15, 20, 25}}, 589 | } 590 | 591 | for _, tc := range tests { 592 | testBitmap(tc) 593 | testArray(tc) 594 | } 595 | } 596 | 597 | func TestRemoveRange(t *testing.T) { 598 | a := NewBitmap() 599 | N := int(1e7) 600 | for i := 0; i < N; i++ { 601 | a.Set(uint64(i)) 602 | } 603 | a.RemoveRange(0, 0) 604 | require.Equal(t, N, a.GetCardinality()) 605 | 606 | require.Equal(t, N, a.GetCardinality()) 607 | a.RemoveRange(uint64(N/4), uint64(N/2)) 608 | require.Equal(t, 3*N/4, a.GetCardinality()) 609 | 610 | a.RemoveRange(0, uint64(N/2)) 611 | require.Equal(t, N/2, a.GetCardinality()) 612 | 613 | a.RemoveRange(uint64(N/2), uint64(N)) 614 | require.Equal(t, 0, a.GetCardinality()) 615 | a.Set(uint64(N / 4)) 616 | a.Set(uint64(N / 2)) 617 | a.Set(uint64(3 * N / 4)) 618 | require.Equal(t, 3, a.GetCardinality()) 619 | 620 | var arr []uint64 621 | for i := 0; i < 123; i++ { 622 | arr = append(arr, uint64(i)) 623 | } 624 | b := FromSortedList(arr) 625 | b.RemoveRange(50, math.MaxUint64) 626 | require.Equal(t, 50, b.GetCardinality()) 627 | } 628 | 629 | func TestRemoveRange2(t *testing.T) { 630 | // High from the last container should not be removed. 631 | a := NewBitmap() 632 | for i := 1; i < 10; i++ { 633 | a.Set(uint64(i * (1 << 16))) 634 | a.Set(uint64(i*(1<<16)) - 1) 635 | } 636 | a.RemoveRange(1<<16, (4<<16)-1) 637 | require.True(t, a.Contains((4<<16)-1)) 638 | } 639 | 640 | func TestSelect(t *testing.T) { 641 | a := NewBitmap() 642 | N := int(1e4) 643 | for i := 0; i < N; i++ { 644 | a.Set(uint64(i)) 645 | } 646 | for i := 0; i < N; i++ { 647 | val, err := a.Select(uint64(i)) 648 | require.NoError(t, err) 649 | require.Equal(t, uint64(i), val) 650 | } 651 | } 652 | 653 | func TestClone(t *testing.T) { 654 | a := NewBitmap() 655 | N := int(1e5) 656 | 657 | for i := 0; i < N; i++ { 658 | a.Set(uint64(rand.Int63n(math.MaxInt64))) 659 | } 660 | b := a.Clone() 661 | require.Equal(t, a.GetCardinality(), b.GetCardinality()) 662 | require.Equal(t, a.ToArray(), b.ToArray()) 663 | } 664 | 665 | func TestContainerFull(t *testing.T) { 666 | c := make([]uint16, maxContainerSize) 667 | b := bitmap(c) 668 | b[indexType] = typeBitmap 669 | b[indexSize] = maxContainerSize 670 | for i := 0; i < 1<<16; i++ { 671 | b.add(uint16(i)) 672 | } 673 | require.Equal(t, math.MaxUint16+1, getCardinality(b)) 674 | 675 | c2 := make([]uint16, maxContainerSize) 676 | copy(c2, c) 677 | b2 := bitmap(c2) 678 | 679 | b.orBitmap(b2, nil, runInline) 680 | require.Equal(t, math.MaxUint16+1, getCardinality(b)) 681 | 682 | setCardinality(b, invalidCardinality) 683 | b.orBitmap(b2, nil, runInline) 684 | require.Equal(t, invalidCardinality, getCardinality(b)) 685 | 686 | setCardinality(b, b.cardinality()) 687 | require.Equal(t, maxCardinality, getCardinality(b)) 688 | } 689 | 690 | func TestExtremes(t *testing.T) { 691 | a := NewBitmap() 692 | require.Equal(t, uint64(0), a.Minimum()) 693 | require.Equal(t, uint64(0), a.Maximum()) 694 | 695 | a.Set(1) 696 | require.Equal(t, uint64(1), a.Minimum()) 697 | require.Equal(t, uint64(1), a.Maximum()) 698 | 699 | a.Set(100000) 700 | require.Equal(t, uint64(1), a.Minimum()) 701 | require.Equal(t, uint64(100000), a.Maximum()) 702 | 703 | a.Remove(100000) 704 | require.Equal(t, uint64(1), a.Minimum()) 705 | require.Equal(t, uint64(1), a.Maximum()) 706 | 707 | a.Remove(1) 708 | require.Equal(t, uint64(0), a.Minimum()) 709 | require.Equal(t, uint64(0), a.Maximum()) 710 | 711 | a.Set(100000) 712 | require.Equal(t, uint64(100000), a.Minimum()) 713 | require.Equal(t, uint64(100000), a.Maximum()) 714 | } 715 | 716 | func TestCleanup(t *testing.T) { 717 | a := NewBitmap() 718 | n := 10 719 | 720 | for i := 0; i < n; i++ { 721 | a.Set(uint64((i * (1 << 16)))) 722 | } 723 | abuf := a.ToBufferWithCopy() 724 | 725 | require.Equal(t, 10, a.keys.numKeys()) 726 | a.RemoveRange(1<<16, 2*(1<<16)) 727 | require.Equal(t, 9, a.keys.numKeys()) 728 | 729 | a.RemoveRange(6*(1<<16), 8*(1<<16)) 730 | require.Equal(t, 7, a.keys.numKeys()) 731 | 732 | a = FromBufferWithCopy(abuf) 733 | require.Equal(t, 10, a.keys.numKeys()) 734 | a.Remove(6 * (1 << 16)) 735 | a.RemoveRange(7*(1<<16), 9*(1<<16)) 736 | require.Equal(t, 7, a.keys.numKeys()) 737 | 738 | n = int(1e6) 739 | b := NewBitmap() 740 | for i := 0; i < n; i++ { 741 | b.Set(uint64(i)) 742 | } 743 | b.RemoveRange(0, uint64(n/2)) 744 | require.Equal(t, n/2, b.GetCardinality()) 745 | buf := b.ToBuffer() 746 | b = FromBuffer(buf) 747 | require.Equal(t, n/2, b.GetCardinality()) 748 | } 749 | 750 | func TestCleanup2(t *testing.T) { 751 | a := NewBitmap() 752 | n := 10 753 | for i := 0; i < n; i++ { 754 | a.Set(uint64(i * (1 << 16))) 755 | } 756 | require.Equal(t, n, a.GetCardinality()) 757 | require.Equal(t, n, a.keys.numKeys()) 758 | 759 | for i := 0; i < n; i++ { 760 | if i%2 == 1 { 761 | a.Remove(uint64(i * (1 << 16))) 762 | } 763 | } 764 | require.Equal(t, n/2, a.GetCardinality()) 765 | require.Equal(t, n, a.keys.numKeys()) 766 | 767 | a.Cleanup() 768 | require.Equal(t, n/2, a.GetCardinality()) 769 | require.Equal(t, n/2, a.keys.numKeys()) 770 | } 771 | 772 | func TestCleanupSplit(t *testing.T) { 773 | a := NewBitmap() 774 | n := int(1e8) 775 | 776 | for i := 0; i < n; i++ { 777 | a.Set(uint64(i)) 778 | } 779 | 780 | split := func() { 781 | n := a.GetCardinality() 782 | mid, err := a.Select(uint64(n / 2)) 783 | require.NoError(t, err) 784 | 785 | b := a.Clone() 786 | a.RemoveRange(0, mid) 787 | b.RemoveRange(mid, math.MaxUint64) 788 | 789 | require.Equal(t, n, a.GetCardinality()+b.GetCardinality()) 790 | } 791 | for a.GetCardinality() > 1 { 792 | split() 793 | } 794 | } 795 | 796 | func TestIsEmpty(t *testing.T) { 797 | a := NewBitmap() 798 | require.True(t, a.IsEmpty()) 799 | 800 | n := int(1e6) 801 | for i := 0; i < n; i++ { 802 | a.Set(uint64(i)) 803 | } 804 | require.False(t, a.IsEmpty()) 805 | a.RemoveRange(0, math.MaxUint64) 806 | require.True(t, a.IsEmpty()) 807 | } 808 | 809 | func TestRank(t *testing.T) { 810 | a := NewBitmap() 811 | n := int(1e6) 812 | for i := uint64(0); i < uint64(n); i++ { 813 | a.Set(i) 814 | } 815 | for i := 0; i < n; i++ { 816 | require.Equal(t, i, a.Rank(uint64(i))) 817 | } 818 | require.Equal(t, -1, a.Rank(uint64(n))) 819 | 820 | // Check ranks after removing an element. 821 | a.Remove(100) 822 | for i := 0; i < n; i++ { 823 | if i < 100 { 824 | require.Equal(t, i, a.Rank(uint64(i))) 825 | } else if i == 100 { 826 | require.Equal(t, -1, a.Rank(uint64(i))) 827 | } else { 828 | require.Equal(t, i-1, a.Rank(uint64(i))) 829 | } 830 | } 831 | 832 | // Check ranks after removing a range of elements. 833 | a.RemoveRange(0, uint64(1e4)) 834 | for i := 0; i < n; i++ { 835 | if i < 1e4 { 836 | require.Equal(t, -1, a.Rank(uint64(n))) 837 | } else { 838 | require.Equal(t, i-1e4, a.Rank(uint64(i))) 839 | } 840 | } 841 | } 842 | 843 | func TestSplit(t *testing.T) { 844 | run := func(n int) { 845 | r := NewBitmap() 846 | for i := 1; i <= n; i++ { 847 | r.Set(uint64(i)) 848 | } 849 | f := func(start, end uint64) uint64 { return 0 } 850 | 851 | // Split the bitmaps. 852 | bms := r.Split(f, 1<<10) 853 | var csum int 854 | for _, bm := range bms { 855 | csum += bm.GetCardinality() 856 | } 857 | require.Equal(t, n, csum) 858 | 859 | id := uint64(1) 860 | for _, bm := range bms { 861 | itr := bm.NewIterator() 862 | for cur := itr.Next(); cur != 0; cur = itr.Next() { 863 | require.Equal(t, id, cur) 864 | id++ 865 | } 866 | } 867 | } 868 | 869 | run(2) 870 | run(11) 871 | run(1e3) 872 | run(1e6) 873 | } 874 | -------------------------------------------------------------------------------- /container.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Dgraph Labs, Inc. and Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package sroar 18 | 19 | import ( 20 | "fmt" 21 | "math" 22 | "math/bits" 23 | "strings" 24 | ) 25 | 26 | // container uses extra 4 []uint16 in the front as header. 27 | // container[0] is used for storing the size of the container, expressed in Uint16. 28 | // The container size cannot exceed the vicinity of 8KB. At 8KB, we switch from packed arrays to 29 | // bitmaps. We can fit the entire uint16 worth of bitmaps in 8KB (2^16 / 8 = 8 30 | // KB). 31 | 32 | const ( 33 | typeArray uint16 = 0x00 34 | typeBitmap uint16 = 0x01 35 | 36 | // Container header. 37 | indexSize int = 0 38 | indexType int = 1 39 | indexCardinality int = 2 40 | // Index 2 and 3 is used for cardinality. We need 2 uint16s to store cardinality because 41 | // 2^16 will not fit in uint16. 42 | startIdx uint16 = 4 43 | 44 | minContainerSize = 64 // In Uint16. 45 | // Bitmap container can contain 2^16 integers. Each integer would use one bit to represent. 46 | // Given that our data is represented in []uint16s, that'd mean the size of container to store 47 | // it would be divided by 16. 48 | // 4 for header and 4096 for storing bitmap container. In Uint16. 49 | maxContainerSize = 4 + (1<<16)/16 50 | ) 51 | 52 | func dataAt(data []uint16, i int) uint16 { return data[int(startIdx)+i] } 53 | 54 | func incrCardinality(data []uint16) { 55 | cur := getCardinality(data) 56 | if cur+1 > math.MaxUint16 { 57 | data[indexCardinality+1] = 1 58 | } else { 59 | data[indexCardinality]++ 60 | } 61 | } 62 | 63 | var invalidCardinality int = math.MaxUint16 + 10 64 | var maxCardinality int = math.MaxUint16 + 1 65 | 66 | func getCardinality(data []uint16) int { 67 | // This sum has to be done using two ints to avoid overflow. 68 | return int(data[indexCardinality]) + int(data[indexCardinality+1]) 69 | } 70 | 71 | func setCardinality(data []uint16, c int) { 72 | if c > math.MaxUint16 { 73 | data[indexCardinality] = math.MaxUint16 74 | data[indexCardinality+1] = uint16(c - math.MaxUint16) 75 | } else { 76 | data[indexCardinality] = uint16(c) 77 | data[indexCardinality+1] = 0 78 | } 79 | } 80 | 81 | func zeroOutContainer(c []uint16) { 82 | switch c[indexType] { 83 | case typeArray: 84 | array(c).zeroOut() 85 | case typeBitmap: 86 | bitmap(c).zeroOut() 87 | } 88 | } 89 | 90 | func removeRangeContainer(c []uint16, lo, hi uint16) { 91 | switch c[indexType] { 92 | case typeArray: 93 | array(c).removeRange(lo, hi) 94 | case typeBitmap: 95 | bitmap(c).removeRange(lo, hi) 96 | } 97 | } 98 | 99 | func calculateAndSetCardinality(data []uint16) { 100 | if data[indexType] != typeBitmap { 101 | panic("Non-bitmap containers should always have cardinality set correctly") 102 | } 103 | b := bitmap(data) 104 | card := b.cardinality() 105 | setCardinality(b, card) 106 | } 107 | 108 | type array []uint16 109 | 110 | // find returns the index of the first element >= x. 111 | // The index is based on data portion of the container, ignoring startIdx. 112 | // If the element > than all elements present, then N is returned where N = cardinality of the 113 | // container. 114 | func (c array) find(x uint16) int { 115 | N := getCardinality(c) 116 | for i := int(startIdx); i < int(startIdx)+N; i++ { 117 | if len(c) <= int(i) { 118 | panic(fmt.Sprintf("find: %d len(c) %d <= i %d\n", x, len(c), i)) 119 | } 120 | if c[i] >= x { 121 | return int(i - int(startIdx)) 122 | } 123 | } 124 | return N 125 | } 126 | 127 | func (c array) rank(x uint16) int { 128 | N := getCardinality(c) 129 | idx := c.find(x) 130 | if idx == N { 131 | return -1 132 | } 133 | return idx 134 | } 135 | 136 | func (c array) has(x uint16) bool { 137 | N := getCardinality(c) 138 | idx := c.find(x) 139 | if idx == N { 140 | return false 141 | } 142 | return c[int(startIdx)+idx] == x 143 | } 144 | 145 | func (c array) add(x uint16) bool { 146 | idx := c.find(x) 147 | N := getCardinality(c) 148 | offset := int(startIdx) + idx 149 | 150 | if int(idx) < N { 151 | if c[offset] == x { 152 | return false 153 | } 154 | // The entry at offset is the first entry, which is greater than x. Move it to the right. 155 | copy(c[offset+1:], c[offset:]) 156 | } 157 | c[offset] = x 158 | incrCardinality(c) 159 | return true 160 | } 161 | 162 | func (c array) remove(x uint16) bool { 163 | idx := c.find(x) 164 | N := getCardinality(c) 165 | offset := int(startIdx) + idx 166 | 167 | if int(idx) < N { 168 | if c[offset] != x { 169 | return false 170 | } 171 | copy(c[offset:], c[offset+1:]) 172 | setCardinality(c, N-1) 173 | return true 174 | } 175 | return false 176 | } 177 | 178 | func (c array) removeRange(lo, hi uint16) { 179 | if hi < lo { 180 | panic(fmt.Sprintf("args must satisfy lo <= hi, got lo: %d, hi: %d\n", lo, hi)) 181 | } 182 | loIdx := c.find(lo) 183 | hiIdx := c.find(hi) 184 | 185 | st := int(startIdx) 186 | loVal := c[st+loIdx] 187 | N := getCardinality(c) 188 | 189 | // remove range doesn't intersect with any element in the array. 190 | if hi < loVal || loIdx == N { 191 | return 192 | } 193 | if hiIdx == N { 194 | if loIdx > 0 { 195 | c = c[:int(startIdx)+loIdx-1] 196 | } else { 197 | c = c[:int(startIdx)] 198 | } 199 | setCardinality(c, loIdx) 200 | return 201 | } 202 | if c[st+hiIdx] == hi { 203 | hiIdx++ 204 | } 205 | copy(c[st+loIdx:], c[st+hiIdx:]) 206 | setCardinality(c, N-hiIdx+loIdx) 207 | } 208 | 209 | func (c array) zeroOut() { 210 | setCardinality(c, 0) 211 | } 212 | 213 | // TODO: Figure out how memory allocation would work in these situations. Perhaps use allocator here? 214 | func (c array) andArray(other array) []uint16 { 215 | min := min(getCardinality(c), getCardinality(other)) 216 | 217 | setc := c.all() 218 | seto := other.all() 219 | 220 | out := make([]uint16, int(startIdx)+min+1) 221 | num := uint16(intersection2by2(setc, seto, out[startIdx:])) 222 | 223 | // Truncate out to how many values were found. 224 | out = out[:startIdx+num+1] 225 | out[indexType] = typeArray 226 | out[indexSize] = uint16(len(out)) 227 | setCardinality(out, int(num)) 228 | return out 229 | } 230 | 231 | // TODO: We can do this operation in-place on the src array. 232 | func (c array) andNotArray(other array, buf []uint16) []uint16 { 233 | max := getCardinality(c) 234 | out := make([]uint16, int(startIdx)+max+1) 235 | 236 | andRes := array(c.andArray(other)).all() 237 | srcVals := array(c).all() 238 | num := uint16(difference(srcVals, andRes, out[startIdx:])) 239 | 240 | // Truncate out to how many values were found. 241 | out = out[:startIdx+num+1] 242 | out[indexType] = typeArray 243 | out[indexSize] = uint16(len(out)) 244 | setCardinality(out, int(num)) 245 | return out 246 | } 247 | 248 | func (c array) orArray(other array, buf []uint16, runMode int) []uint16 { 249 | // We ignore runInline for this call. 250 | 251 | max := getCardinality(c) + getCardinality(other) 252 | if max > 4096 { 253 | // Use bitmap container. 254 | out := bitmap(c.toBitmapContainer(buf)) 255 | // For now, just keep it as a bitmap. No need to change if the 256 | // cardinality is smaller than 4096. 257 | out.orArray(other, nil, runMode|runInline) 258 | // Return out because out is pointing to buf. This would allow the 259 | // receiver to copy out. 260 | return out 261 | } 262 | 263 | // The output would be of typeArray. 264 | out := buf[:int(startIdx)+max] 265 | num := union2by2(c.all(), other.all(), out[startIdx:]) 266 | out[indexType] = typeArray 267 | out[indexSize] = uint16(len(out)) 268 | setCardinality(out, num) 269 | return out 270 | } 271 | 272 | var tmp = make([]uint16, 8192) 273 | 274 | func (c array) andBitmap(other bitmap) []uint16 { 275 | out := make([]uint16, int(startIdx)+getCardinality(c)+2) // some extra space. 276 | out[indexType] = typeArray 277 | 278 | pos := startIdx 279 | for _, x := range c.all() { 280 | out[pos] = x 281 | pos += other.bitValue(x) 282 | } 283 | 284 | // Ensure we have at least one empty slot at the end. 285 | res := out[:pos+1] 286 | res[indexSize] = uint16(len(res)) 287 | setCardinality(res, int(pos-startIdx)) 288 | return res 289 | } 290 | 291 | // TODO: Write an optmized version of this function. 292 | func (c array) andNotBitmap(other bitmap, buf []uint16) []uint16 { 293 | assert(len(buf) == maxContainerSize) 294 | res := array(buf) 295 | Memclr(res) 296 | res[indexSize] = 4 297 | for _, e := range c.all() { 298 | if !other.has(e) { 299 | res.add(e) 300 | } 301 | } 302 | return res 303 | } 304 | 305 | func (c array) isFull() bool { 306 | N := getCardinality(c) 307 | return int(startIdx)+N >= len(c) 308 | } 309 | 310 | func (c array) all() []uint16 { 311 | N := getCardinality(c) 312 | return c[startIdx : int(startIdx)+N] 313 | } 314 | 315 | func (c array) minimum() uint16 { 316 | N := getCardinality(c) 317 | if N == 0 { 318 | return 0 319 | } 320 | return c[startIdx] 321 | } 322 | 323 | func (c array) maximum() uint16 { 324 | N := getCardinality(c) 325 | if N == 0 { 326 | return 0 327 | } 328 | return c[int(startIdx)+N-1] 329 | } 330 | 331 | func (c array) toBitmapContainer(buf []uint16) []uint16 { 332 | if len(buf) == 0 { 333 | buf = make([]uint16, maxContainerSize) 334 | } else { 335 | assert(len(buf) == maxContainerSize) 336 | assert(len(buf) == copy(buf, empty)) 337 | } 338 | 339 | b := bitmap(buf) 340 | b[indexSize] = maxContainerSize 341 | b[indexType] = typeBitmap 342 | setCardinality(b, getCardinality(c)) 343 | 344 | data := b[startIdx:] 345 | for _, x := range c.all() { 346 | idx := x >> 4 347 | pos := x & 0xF 348 | data[idx] |= bitmapMask[pos] 349 | } 350 | return b 351 | } 352 | 353 | func (c array) String() string { 354 | var b strings.Builder 355 | b.WriteString(fmt.Sprintf("Size: %d\n", c[0])) 356 | for i, val := range c[startIdx:] { 357 | b.WriteString(fmt.Sprintf("%d: %d\n", i, val)) 358 | } 359 | return b.String() 360 | } 361 | 362 | type bitmap []uint16 363 | 364 | var bitmapMask []uint16 365 | 366 | func init() { 367 | bitmapMask = make([]uint16, 16) 368 | for i := 0; i < 16; i++ { 369 | bitmapMask[i] = 1 << (15 - i) 370 | } 371 | } 372 | 373 | func (b bitmap) add(x uint16) bool { 374 | idx := x >> 4 375 | pos := x & 0xF 376 | 377 | if has := b[startIdx+idx] & bitmapMask[pos]; has > 0 { 378 | return false 379 | } 380 | 381 | b[startIdx+idx] |= bitmapMask[pos] 382 | incrCardinality(b) 383 | return true 384 | } 385 | 386 | func (b bitmap) remove(x uint16) bool { 387 | idx := x >> 4 388 | pos := x & 0xF 389 | 390 | c := getCardinality(b) 391 | if has := b[startIdx+idx] & bitmapMask[pos]; has > 0 { 392 | b[startIdx+idx] ^= bitmapMask[pos] 393 | setCardinality(b, c-1) 394 | return true 395 | } 396 | return false 397 | } 398 | 399 | func (b bitmap) removeRange(lo, hi uint16) { 400 | loIdx := lo >> 4 401 | loPos := lo & 0xF 402 | 403 | hiIdx := hi >> 4 404 | hiPos := hi & 0xF 405 | 406 | N := getCardinality(b) 407 | var removed int 408 | for i := loIdx + 1; i < hiIdx; i++ { 409 | removed += bits.OnesCount16(b[startIdx+i]) 410 | b[startIdx+i] = 0 411 | } 412 | 413 | if loIdx == hiIdx { 414 | for p := loPos; p <= hiPos; p++ { 415 | if b[startIdx+loIdx]&bitmapMask[p] > 0 { 416 | removed++ 417 | } 418 | b[startIdx+loIdx] &= ^bitmapMask[p] 419 | } 420 | setCardinality(b, N-removed) 421 | return 422 | } 423 | for p := loPos; p < 1<<4; p++ { 424 | if b[startIdx+loIdx]&bitmapMask[p] > 0 { 425 | removed++ 426 | } 427 | b[startIdx+loIdx] &= ^bitmapMask[p] 428 | } 429 | for p := uint16(0); p <= hiPos; p++ { 430 | if b[startIdx+hiIdx]&bitmapMask[p] > 0 { 431 | removed++ 432 | } 433 | b[startIdx+hiIdx] &= ^bitmapMask[p] 434 | } 435 | setCardinality(b, N-removed) 436 | } 437 | 438 | func (b bitmap) has(x uint16) bool { 439 | idx := x >> 4 440 | pos := x & 0xF 441 | has := b[startIdx+idx] & bitmapMask[pos] 442 | return has > 0 443 | } 444 | 445 | func (b bitmap) rank(x uint16) int { 446 | idx := x >> 4 447 | pos := x & 0xF 448 | if b[startIdx+idx]&bitmapMask[pos] == 0 { 449 | return -1 450 | } 451 | 452 | var rank int 453 | for i := 0; i < int(idx); i++ { 454 | rank += bits.OnesCount16(b[int(startIdx)+i]) 455 | } 456 | for p := uint16(0); p <= pos; p++ { 457 | if b[startIdx+idx]&bitmapMask[p] > 0 { 458 | rank++ 459 | } 460 | } 461 | return rank - 1 462 | } 463 | 464 | // TODO: This can perhaps be using SIMD instructions. 465 | func (b bitmap) andBitmap(other bitmap) []uint16 { 466 | out := make([]uint16, maxContainerSize) 467 | out[indexSize] = maxContainerSize 468 | out[indexType] = typeBitmap 469 | var num int 470 | for i := int(startIdx); i < len(b); i++ { 471 | out[i] = b[i] & other[i] 472 | num += bits.OnesCount16(out[i]) 473 | } 474 | setCardinality(out, num) 475 | return out 476 | } 477 | 478 | func (b bitmap) orBitmap(other bitmap, buf []uint16, runMode int) []uint16 { 479 | if runMode&runInline > 0 { 480 | buf = b 481 | } else { 482 | copy(buf, b) // Copy over first. 483 | } 484 | buf[indexSize] = maxContainerSize 485 | buf[indexType] = typeBitmap 486 | 487 | if num := getCardinality(b); num == maxCardinality { 488 | // do nothing. bitmap is already full. 489 | 490 | } else if runMode&runLazy > 0 || num == invalidCardinality { 491 | data := buf[startIdx:] 492 | for i, v := range other[startIdx:] { 493 | data[i] |= v 494 | } 495 | setCardinality(buf, invalidCardinality) 496 | 497 | } else { 498 | var num int 499 | data := buf[startIdx:] 500 | for i, v := range other[startIdx:] { 501 | data[i] |= v 502 | // We are going to iterate over the entire container. So, we can 503 | // just recount the cardinality, starting from num=0. 504 | num += bits.OnesCount16(data[i]) 505 | } 506 | setCardinality(buf, num) 507 | } 508 | if runMode&runInline > 0 { 509 | return nil 510 | } 511 | return buf 512 | } 513 | 514 | func (b bitmap) andNotBitmap(other bitmap) []uint16 { 515 | var num int 516 | data := b[startIdx:] 517 | for i, v := range other[startIdx:] { 518 | data[i] = data[i] ^ (data[i] & v) 519 | num += bits.OnesCount16(data[i]) 520 | } 521 | setCardinality(b, num) 522 | return b 523 | } 524 | 525 | func (b bitmap) andNotArray(other array) []uint16 { 526 | for _, e := range other.all() { 527 | b.remove(e) 528 | } 529 | return b 530 | } 531 | 532 | func (b bitmap) orArray(other array, buf []uint16, runMode int) []uint16 { 533 | if runMode&runInline > 0 { 534 | buf = b 535 | } else { 536 | copy(buf, b) 537 | } 538 | 539 | if num := getCardinality(b); num == maxCardinality { 540 | // do nothing. This bitmap is already full. 541 | 542 | } else if runMode&runLazy > 0 || num == invalidCardinality { 543 | // Avoid calculating the cardinality to speed up operations. 544 | for _, x := range other.all() { 545 | idx := x / 16 546 | pos := x % 16 547 | 548 | buf[startIdx+idx] |= bitmapMask[pos] 549 | } 550 | setCardinality(buf, invalidCardinality) 551 | 552 | } else { 553 | num := getCardinality(buf) 554 | for _, x := range other.all() { 555 | idx := x / 16 556 | pos := x % 16 557 | 558 | val := &buf[4+idx] 559 | before := bits.OnesCount16(*val) 560 | *val |= bitmapMask[pos] 561 | after := bits.OnesCount16(*val) 562 | num += after - before 563 | } 564 | setCardinality(buf, num) 565 | } 566 | 567 | if runMode&runInline > 0 { 568 | return nil 569 | } 570 | return buf 571 | } 572 | 573 | func (b bitmap) all() []uint16 { 574 | var res []uint16 575 | data := b[startIdx:] 576 | for idx := uint16(0); idx < uint16(len(data)); idx++ { 577 | x := data[idx] 578 | // TODO: This could potentially be optimized. 579 | for pos := uint16(0); pos < 16; pos++ { 580 | if x&bitmapMask[pos] > 0 { 581 | res = append(res, (idx<<4)|pos) 582 | } 583 | } 584 | } 585 | return res 586 | } 587 | 588 | //TODO: It can be optimized. 589 | func (b bitmap) selectAt(idx int) uint16 { 590 | data := b[startIdx:] 591 | n := uint16(len(data)) 592 | for i := uint16(0); i < n; i++ { 593 | x := data[i] 594 | c := bits.OnesCount16(x) 595 | if idx < c { 596 | for pos := uint16(0); pos < 16; pos++ { 597 | if idx == 0 && x&bitmapMask[pos] > 0 { 598 | return i*16 + pos 599 | } 600 | if x&bitmapMask[pos] > 0 { 601 | idx-- 602 | } 603 | } 604 | 605 | } 606 | idx -= c 607 | } 608 | panic("should not reach here") 609 | } 610 | 611 | // bitValue returns a 0 or a 1 depending upon whether x is present in the bitmap, where 1 means 612 | // present and 0 means absent. 613 | func (b bitmap) bitValue(x uint16) uint16 { 614 | idx := x >> 4 615 | return (b[4+idx] >> (15 - (x & 0xF))) & 1 616 | } 617 | 618 | func (b bitmap) isFull() bool { 619 | return false 620 | } 621 | 622 | func (b bitmap) minimum() uint16 { 623 | N := getCardinality(b) 624 | if N == 0 { 625 | return 0 626 | } 627 | for i, x := range b[startIdx:] { 628 | lz := bits.LeadingZeros16(x) 629 | if lz == 16 { 630 | continue 631 | } 632 | return uint16(16*i + lz) 633 | } 634 | panic("We shouldn't reach here") 635 | } 636 | 637 | func (b bitmap) maximum() uint16 { 638 | N := getCardinality(b) 639 | if N == 0 { 640 | return 0 641 | } 642 | for i := len(b) - 1; i >= int(startIdx); i-- { 643 | x := b[i] 644 | tz := bits.TrailingZeros16(x) 645 | if tz == 16 { 646 | continue 647 | } 648 | return uint16(16*i + 15 - tz) 649 | } 650 | panic("We shouldn't reach here") 651 | } 652 | 653 | func (b bitmap) cardinality() int { 654 | var num int 655 | for _, x := range b[startIdx:] { 656 | num += bits.OnesCount16(x) 657 | } 658 | return num 659 | } 660 | 661 | var zeroContainer = make([]uint16, maxContainerSize) 662 | 663 | func (b bitmap) zeroOut() { 664 | setCardinality(b, 0) 665 | copy(b[startIdx:], zeroContainer[startIdx:]) 666 | } 667 | 668 | var ( 669 | runInline = 0x01 670 | runLazy = 0x02 671 | ) 672 | 673 | func containerOr(ac, bc, buf []uint16, runMode int) []uint16 { 674 | at := ac[indexType] 675 | bt := bc[indexType] 676 | 677 | if at == typeArray && bt == typeArray { 678 | left := array(ac) 679 | right := array(bc) 680 | // We can't always inline this function. If the right container has 681 | // enough entries, trying to do a union with the left container inplace 682 | // could end up overwriting the left container entries. So, we use a 683 | // buffer to hold all output, and then copy it over to left. 684 | // 685 | // TODO: If right doesn't have a lot of entries, we could just iterate 686 | // over left and merge the entries from right inplace. Would be faster 687 | // than copying over all entries into buffer. Worth trying that approach. 688 | return left.orArray(right, buf, runMode) 689 | } 690 | if at == typeArray && bt == typeBitmap { 691 | left := array(ac) 692 | right := bitmap(bc) 693 | // Don't run inline for this call. 694 | return right.orArray(left, buf, runMode&^runInline) 695 | } 696 | 697 | // These two following cases can be fully inlined. 698 | if at == typeBitmap && bt == typeArray { 699 | left := bitmap(ac) 700 | right := array(bc) 701 | return left.orArray(right, buf, runMode) 702 | } 703 | if at == typeBitmap && bt == typeBitmap { 704 | left := bitmap(ac) 705 | right := bitmap(bc) 706 | return left.orBitmap(right, buf, runMode) 707 | } 708 | panic("containerOr: We should not reach here") 709 | } 710 | 711 | func containerAnd(ac, bc []uint16) []uint16 { 712 | at := ac[indexType] 713 | bt := bc[indexType] 714 | 715 | if at == typeArray && bt == typeArray { 716 | left := array(ac) 717 | right := array(bc) 718 | return left.andArray(right) 719 | } 720 | if at == typeArray && bt == typeBitmap { 721 | left := array(ac) 722 | right := bitmap(bc) 723 | return left.andBitmap(right) 724 | } 725 | if at == typeBitmap && bt == typeArray { 726 | left := bitmap(ac) 727 | right := array(bc) 728 | out := right.andBitmap(left) 729 | return out 730 | } 731 | if at == typeBitmap && bt == typeBitmap { 732 | left := bitmap(ac) 733 | right := bitmap(bc) 734 | return left.andBitmap(right) 735 | } 736 | panic("containerAnd: We should not reach here") 737 | } 738 | 739 | // TODO: Optimize this function. 740 | func containerAndNot(ac, bc, buf []uint16) []uint16 { 741 | at := ac[indexType] 742 | bt := bc[indexType] 743 | 744 | if at == typeArray && bt == typeArray { 745 | left := array(ac) 746 | right := array(bc) 747 | return left.andNotArray(right, buf) 748 | } 749 | if at == typeArray && bt == typeBitmap { 750 | left := array(ac) 751 | right := bitmap(bc) 752 | return left.andNotBitmap(right, buf) 753 | } 754 | if at == typeBitmap && bt == typeArray { 755 | left := bitmap(ac) 756 | right := array(bc) 757 | out := left.andNotArray(right) 758 | return out 759 | } 760 | if at == typeBitmap && bt == typeBitmap { 761 | left := bitmap(ac) 762 | right := bitmap(bc) 763 | return left.andNotBitmap(right) 764 | } 765 | panic("containerAndNot: We should not reach here") 766 | } 767 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dgraph-io/sroar 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/RoaringBitmap/roaring v0.6.1 7 | github.com/davecgh/go-spew v1.1.1 // indirect 8 | github.com/pkg/errors v0.9.1 9 | github.com/stretchr/testify v1.7.0 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/RoaringBitmap/roaring v0.6.1 h1:O36Tdaj1Fi/zyr25shTHwlQPGdq53+u4WkM08AOEjiE= 2 | github.com/RoaringBitmap/roaring v0.6.1/go.mod h1:WZ83fjBF/7uBHi6QoFyfGL4+xuV4Qn+xFkm4+vSzrhE= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= 7 | github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= 8 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 9 | github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 10 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 11 | github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY= 12 | github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= 13 | github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= 14 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 15 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 16 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 17 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 18 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 19 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 20 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 21 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 22 | github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= 23 | github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= 24 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 25 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 26 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 27 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 28 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 29 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 30 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 31 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 32 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 33 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 34 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 35 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 36 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 37 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 38 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 39 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 40 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 41 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 42 | golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= 43 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 44 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 45 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 46 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 47 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 48 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 49 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 50 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 51 | -------------------------------------------------------------------------------- /iterator.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Dgraph Labs, Inc. and Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package sroar 18 | 19 | import ( 20 | "math/bits" 21 | ) 22 | 23 | type Iterator struct { 24 | bm *Bitmap 25 | 26 | keys []uint64 27 | keyIdx int 28 | 29 | contIdx int 30 | 31 | bitmapIdx int 32 | bitset uint16 33 | } 34 | 35 | func (bm *Bitmap) NewRangeIterators(numRanges int) []*Iterator { 36 | keyn := bm.keys.numKeys() 37 | iters := make([]*Iterator, numRanges) 38 | width := keyn / numRanges 39 | rem := keyn % numRanges 40 | cnt := 0 41 | 42 | // This loop distributes the key equally to the ranges. For example: If numRanges = 3 43 | // and keyn = 8 then it will be distributes as [3, 3, 2] 44 | for i := 0; i < numRanges; i++ { 45 | iters[i] = bm.NewIterator() 46 | n := width 47 | if i < rem { 48 | n = width + 1 49 | } 50 | iters[i].keys = iters[i].keys[cnt : cnt+2*n] 51 | cnt = cnt + 2*n 52 | } 53 | return iters 54 | } 55 | 56 | func (bm *Bitmap) NewIterator() *Iterator { 57 | return &Iterator{ 58 | bm: bm, 59 | keys: bm.keys[indexNodeStart : indexNodeStart+bm.keys.numKeys()*2], 60 | keyIdx: 0, 61 | contIdx: -1, 62 | bitmapIdx: -1, 63 | } 64 | } 65 | 66 | func (it *Iterator) Next() uint64 { 67 | if len(it.keys) == 0 { 68 | return 0 69 | } 70 | 71 | key := it.keys[it.keyIdx] 72 | off := it.keys[it.keyIdx+1] 73 | cont := it.bm.getContainer(off) 74 | card := getCardinality(cont) 75 | 76 | // Loop until we find a container on which next operation is possible. When such a container 77 | // is found, reset the variables responsible for container iteration. 78 | for card == 0 || it.contIdx+1 >= card { 79 | if it.keyIdx+2 >= len(it.keys) { 80 | return 0 81 | } 82 | // jump by 2 because key is followed by a value 83 | it.keyIdx += 2 84 | it.contIdx = -1 85 | it.bitmapIdx = -1 86 | it.bitset = 0 87 | key = it.keys[it.keyIdx] 88 | off = it.keys[it.keyIdx+1] 89 | cont = it.bm.getContainer(off) 90 | card = getCardinality(cont) 91 | } 92 | 93 | // The above loop assures that we can do next in this container. 94 | it.contIdx++ 95 | switch cont[indexType] { 96 | case typeArray: 97 | return key | uint64(cont[int(startIdx)+it.contIdx]) 98 | case typeBitmap: 99 | // A bitmap container is an array of uint16s. 100 | // If the container is bitmap, go to the index which has a non-zero value. 101 | for it.bitset == 0 && it.bitmapIdx+1 < len(cont[startIdx:]) { 102 | it.bitmapIdx++ 103 | it.bitset = cont[int(startIdx)+it.bitmapIdx] 104 | } 105 | assert(it.bitset > 0) 106 | 107 | // msbIdx is the index of most-significant bit. In this iteration we choose this set bit 108 | // and make it zero. 109 | msbIdx := uint16(bits.LeadingZeros16(it.bitset)) 110 | msb := 1 << (16 - msbIdx - 1) 111 | it.bitset ^= uint16(msb) 112 | return key | uint64(it.bitmapIdx*16+int(msbIdx)) 113 | } 114 | return 0 115 | } 116 | 117 | type ManyItr struct { 118 | index int 119 | arr []uint64 120 | } 121 | 122 | // TODO: See if this is needed, we should remove this 123 | func (r *Bitmap) ManyIterator() *ManyItr { 124 | return &ManyItr{ 125 | arr: r.ToArray(), 126 | } 127 | 128 | } 129 | 130 | func (itr *ManyItr) NextMany(buf []uint64) int { 131 | count := 0 132 | for i := 0; i < len(buf); i++ { 133 | if itr.index == len(itr.arr) { 134 | break 135 | } 136 | buf[i] = itr.arr[itr.index] 137 | itr.index++ 138 | count++ 139 | } 140 | return count 141 | } 142 | -------------------------------------------------------------------------------- /iterator_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Dgraph Labs, Inc. and Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package sroar 18 | 19 | import ( 20 | "math/rand" 21 | "sort" 22 | "testing" 23 | 24 | "github.com/stretchr/testify/require" 25 | ) 26 | 27 | func TestIteratorBasic(t *testing.T) { 28 | n := uint64(1e5) 29 | bm := NewBitmap() 30 | for i := uint64(1); i <= n; i++ { 31 | bm.Set(uint64(i)) 32 | } 33 | 34 | it := bm.NewIterator() 35 | for i := uint64(1); i <= n; i++ { 36 | v := it.Next() 37 | require.Equal(t, i, v) 38 | } 39 | v := it.Next() 40 | require.Equal(t, uint64(0), v) 41 | } 42 | 43 | func TestIteratorRanges(t *testing.T) { 44 | n := uint64(1e5) 45 | bm := NewBitmap() 46 | for i := uint64(1); i <= n; i++ { 47 | bm.Set(uint64(i)) 48 | } 49 | 50 | iters := bm.NewRangeIterators(8) 51 | cnt := uint64(1) 52 | for idx := 0; idx < 8; idx++ { 53 | it := iters[idx] 54 | for v := it.Next(); v > 0; v = it.Next() { 55 | require.Equal(t, cnt, v) 56 | cnt++ 57 | } 58 | } 59 | } 60 | 61 | func TestIteratorRandom(t *testing.T) { 62 | n := uint64(1e6) 63 | bm := NewBitmap() 64 | mp := make(map[uint64]struct{}) 65 | var arr []uint64 66 | for i := uint64(1); i <= n; i++ { 67 | v := uint64(rand.Intn(int(n) * 5)) 68 | if v == 0 { 69 | continue 70 | } 71 | if _, ok := mp[v]; ok { 72 | continue 73 | } 74 | mp[v] = struct{}{} 75 | arr = append(arr, v) 76 | bm.Set(uint64(v)) 77 | } 78 | 79 | sort.Slice(arr, func(i, j int) bool { 80 | return arr[i] < arr[j] 81 | }) 82 | 83 | it := bm.NewIterator() 84 | v := it.Next() 85 | for i := uint64(0); i < uint64(len(arr)); i++ { 86 | require.Equal(t, arr[i], v) 87 | v = it.Next() 88 | } 89 | } 90 | 91 | func TestIteratorWithRemoveKeys(t *testing.T) { 92 | b := NewBitmap() 93 | N := uint64(1e6) 94 | for i := uint64(0); i < N; i++ { 95 | b.Set(i) 96 | } 97 | 98 | b.RemoveRange(0, N) 99 | it := b.NewIterator() 100 | 101 | cnt := 0 102 | for it.Next() > 0 { 103 | cnt++ 104 | } 105 | require.Equal(t, 0, cnt) 106 | } 107 | 108 | func TestManyIterator(t *testing.T) { 109 | b := NewBitmap() 110 | for i := 0; i < int(1e6); i++ { 111 | b.Set(uint64(i)) 112 | } 113 | 114 | mi := b.ManyIterator() 115 | buf := make([]uint64, 1000) 116 | 117 | i := 0 118 | for { 119 | got := mi.NextMany(buf) 120 | if got == 0 { 121 | break 122 | } 123 | require.Equal(t, 1000, got) 124 | require.Equal(t, uint64(i*1000), buf[0]) 125 | i++ 126 | } 127 | } 128 | 129 | func BenchmarkIterator(b *testing.B) { 130 | bm := NewBitmap() 131 | for i := 0; i < int(1e5); i++ { 132 | bm.Set(uint64(i)) 133 | } 134 | 135 | b.ResetTimer() 136 | for i := 0; i < b.N; i++ { 137 | it := bm.NewIterator() 138 | for it.Next() > 0 { 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /keys.go: -------------------------------------------------------------------------------- 1 | package sroar 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | var ( 9 | indexNodeSize = 0 10 | indexNumKeys = 1 11 | indexNodeStart = 2 12 | ) 13 | 14 | // node stores uint64 keys and the corresponding container offset in the buffer. 15 | // 0th index (indexNodeSize) is used for storing the size of node in bytes. 16 | // 1st index (indexNumKeys) is used for storing the number of keys. 17 | // 2nd index is where we start writing the key-value pairs. 18 | type node []uint64 19 | 20 | func keyOffset(i int) int { return indexNodeStart + 2*i } 21 | func valOffset(i int) int { return indexNodeStart + 2*i + 1 } 22 | 23 | func (n node) numKeys() int { return int(n[indexNumKeys]) } 24 | func (n node) size() int { return int(n[indexNodeSize]) } 25 | func (n node) maxKeys() int { return (len(n) - indexNodeStart) / 2 } 26 | func (n node) key(i int) uint64 { return n[keyOffset(i)] } 27 | func (n node) val(i int) uint64 { return n[valOffset(i)] } 28 | func (n node) data(i int) []uint64 { return n[keyOffset(i):keyOffset(i+1)] } 29 | 30 | func (n node) uint64(idx int) uint64 { return n[idx] } 31 | func (n node) setAt(idx int, k uint64) { n[idx] = k } 32 | 33 | func (n node) setNumKeys(num int) { n[indexNumKeys] = uint64(num) } 34 | func (n node) setNodeSize(sz int) { n[indexNodeSize] = uint64(sz) } 35 | 36 | func (n node) maxKey() uint64 { 37 | idx := n.numKeys() 38 | // numKeys == index of the max key, because 0th index is being used for meta information. 39 | if idx == 0 { 40 | return 0 41 | } 42 | return n.key(idx) 43 | } 44 | 45 | func (n node) moveRight(lo int) { 46 | hi := n.numKeys() 47 | assert(!n.isFull()) 48 | // copy works despite of overlap in src and dst. 49 | // See https://golang.org/pkg/builtin/#copy 50 | copy(n[keyOffset(lo+1):keyOffset(hi+1)], n[keyOffset(lo):keyOffset(hi)]) 51 | } 52 | 53 | // isFull checks that the node is already full. 54 | func (n node) isFull() bool { 55 | return n.numKeys() == n.maxKeys() 56 | } 57 | 58 | // Search returns the index of a smallest key >= k in a node. 59 | func (n node) search(k uint64) int { 60 | N := n.numKeys() 61 | lo, hi := 0, N-1 62 | for lo+16 <= hi { 63 | mid := lo + (hi-lo)/2 64 | ki := n.key(mid) 65 | // fmt.Printf("lo: %d mid: %d hi: %d. ki: %#x k: %#x\n", lo, mid, hi, ki, k) 66 | 67 | if ki < k { 68 | lo = mid + 1 69 | } else if ki > k { 70 | hi = mid 71 | // We should keep it equal, and not -1, because we'll take the first greater entry. 72 | } else { 73 | // fmt.Printf("returning mid: %d\n", mid) 74 | return mid 75 | } 76 | } 77 | for ; lo <= hi; lo++ { 78 | ki := n.key(lo) 79 | // fmt.Printf("itr. lo: %d hi: %d. ki: %#x k: %#x\n", lo, hi, ki, k) 80 | if ki >= k { 81 | return lo 82 | } 83 | } 84 | return N 85 | // if N < 4 { 86 | // simd.Search has a bug which causes this to return index 11 when it should be returning index 87 | // 9. 88 | // } 89 | // return int(simd.Search(n[keyOffset(0):keyOffset(N)], k)) 90 | } 91 | 92 | func zeroOut(data []uint64) { 93 | for i := 0; i < len(data); i++ { 94 | data[i] = 0 95 | } 96 | } 97 | 98 | // compacts the node i.e., remove all the kvs with value < lo. It returns the remaining number of 99 | // keys. 100 | func (n node) compact(lo uint64) int { 101 | N := n.numKeys() 102 | mk := n.maxKey() 103 | var left, right int 104 | for right = 0; right < N; right++ { 105 | if n.val(right) < lo && n.key(right) < mk { 106 | // Skip over this key. Don't copy it. 107 | continue 108 | } 109 | // Valid data. Copy it from right to left. Advance left. 110 | if left != right { 111 | copy(n.data(left), n.data(right)) 112 | } 113 | left++ 114 | } 115 | // zero out rest of the kv pairs. 116 | zeroOut(n[keyOffset(left):keyOffset(right)]) 117 | n.setNumKeys(left) 118 | 119 | // If the only key we have is the max key, and its value is less than lo, then we can indicate 120 | // to the caller by returning a zero that it's OK to drop the node. 121 | if left == 1 && n.key(0) == mk && n.val(0) < lo { 122 | return 0 123 | } 124 | return left 125 | } 126 | 127 | // getValue returns the value corresponding to the key if found. 128 | func (n node) getValue(k uint64) (uint64, bool) { 129 | k &= mask // Ensure k has its lowest bits unset. 130 | idx := n.search(k) 131 | // key is not found 132 | if idx >= n.numKeys() { 133 | return 0, false 134 | } 135 | if ki := n.key(idx); ki == k { 136 | return n.val(idx), true 137 | } 138 | return 0, false 139 | } 140 | 141 | // set returns true if it added a new key. 142 | func (n node) set(k, v uint64) bool { 143 | N := n.numKeys() 144 | idx := n.search(k) 145 | if idx == N { 146 | n.setNumKeys(N + 1) 147 | n.setAt(keyOffset(idx), k) 148 | n.setAt(valOffset(idx), v) 149 | return true 150 | } 151 | 152 | ki := n.key(idx) 153 | if N == n.maxKeys() { 154 | // This happens during split of non-root node, when we are updating the child pointer of 155 | // right node. Hence, the key should already exist. 156 | assert(ki == k) 157 | } 158 | if ki == k { 159 | n.setAt(valOffset(idx), v) 160 | return false 161 | } 162 | assert(ki > k) 163 | // Found the first entry which is greater than k. So, we need to fit k 164 | // just before it. For that, we should move the rest of the data in the 165 | // node to the right to make space for k. 166 | n.moveRight(idx) 167 | n.setNumKeys(N + 1) 168 | n.setAt(keyOffset(idx), k) 169 | n.setAt(valOffset(idx), v) 170 | return true 171 | // panic("shouldn't reach here") 172 | } 173 | 174 | func (n node) updateOffsets(beyond, by uint64, add bool) { 175 | for i := 0; i < n.numKeys(); i++ { 176 | if offset := n.val(i); offset > beyond { 177 | if add { 178 | n.setAt(valOffset(i), offset+by) 179 | } else { 180 | assert(offset >= by) 181 | n.setAt(valOffset(i), offset-by) 182 | } 183 | } 184 | } 185 | } 186 | 187 | func (n node) iterate(fn func(node, int)) { 188 | for i := 0; i < n.maxKeys(); i++ { 189 | if k := n.key(i); k > 0 { 190 | fn(n, i) 191 | } else { 192 | break 193 | } 194 | } 195 | } 196 | 197 | func (n node) print(parentID uint64) { 198 | var keys []string 199 | n.iterate(func(n node, i int) { 200 | keys = append(keys, fmt.Sprintf("%d", n.key(i))) 201 | }) 202 | if len(keys) > 8 { 203 | copy(keys[4:], keys[len(keys)-4:]) 204 | keys[3] = "..." 205 | keys = keys[:8] 206 | } 207 | fmt.Printf("num keys: %d keys: %s\n", n.numKeys(), strings.Join(keys, " ")) 208 | } 209 | -------------------------------------------------------------------------------- /real_data_test.go: -------------------------------------------------------------------------------- 1 | // +build real 2 | 3 | package sroar 4 | 5 | import ( 6 | "archive/zip" 7 | "bytes" 8 | "fmt" 9 | "io" 10 | "os" 11 | "path" 12 | "strconv" 13 | "strings" 14 | "testing" 15 | 16 | "github.com/pkg/errors" 17 | "github.com/stretchr/testify/require" 18 | ) 19 | 20 | // To run these benchmarks: go test -bench BenchmarkRealDataFastOr -run - 21 | 22 | var realDatasets = []string{ 23 | "census-income_srt", "census-income", "census1881_srt", "census1881", 24 | "dimension_003", "dimension_008", "dimension_033", "uscensus2000", "weather_sept_85_srt", 25 | "weather_sept_85", "wikileaks-noquotes_srt", "wikileaks-noquotes", 26 | } 27 | 28 | func getDataSetPath(dataset string) (string, error) { 29 | gopath, ok := os.LookupEnv("GOPATH") 30 | if !ok { 31 | return "", fmt.Errorf("GOPATH not set. It's required to locate real-roaring-dataset.") 32 | } 33 | 34 | basePath := path.Join(gopath, "src", "github.com", "RoaringBitmap", "real-roaring-datasets") 35 | if _, err := os.Stat(basePath); os.IsNotExist(err) { 36 | return "", fmt.Errorf("real-roaring-datasets does not exist. " + 37 | "Run `go get github.com/RoaringBitmap/real-roaring-datasets`") 38 | } 39 | 40 | datasetPath := path.Join(basePath, dataset+".zip") 41 | if _, err := os.Stat(datasetPath); os.IsNotExist(err) { 42 | return "", fmt.Errorf("dataset %s does not exist, tried path: %s", 43 | dataset, datasetPath) 44 | } 45 | return datasetPath, nil 46 | } 47 | 48 | func retrieveRealDataBitmaps(datasetName string, optimize bool) ([]*Bitmap, error) { 49 | datasetPath, err := getDataSetPath(datasetName) 50 | zipFile, err := zip.OpenReader(datasetPath) 51 | if err != nil { 52 | return nil, fmt.Errorf("error opening dataset %s zipfile, cause: %v", datasetPath, err) 53 | } 54 | defer zipFile.Close() 55 | 56 | bitmaps := make([]*Bitmap, len(zipFile.File)) 57 | for i, f := range zipFile.File { 58 | res, err := processZipFile(f) 59 | if err != nil { 60 | return nil, errors.Wrap(err, "while processing zip file") 61 | } 62 | b := NewBitmap() 63 | for _, v := range res { 64 | b.Set(v) 65 | } 66 | bitmaps[i] = b 67 | } 68 | 69 | return bitmaps, nil 70 | } 71 | 72 | func processZipFile(f *zip.File) ([]uint64, error) { 73 | r, err := f.Open() 74 | if err != nil { 75 | return nil, fmt.Errorf("failed to read bitmap file %s, cause: %v", 76 | f.Name, err) 77 | } 78 | 79 | buf := make([]byte, f.UncompressedSize) 80 | var bufStep uint64 = 32768 // apparently the largest buffer zip can read 81 | var totalReadBytes uint64 82 | 83 | for { 84 | var endOffset uint64 85 | if f.UncompressedSize64 < totalReadBytes+bufStep { 86 | endOffset = f.UncompressedSize64 87 | } else { 88 | endOffset = totalReadBytes + bufStep 89 | } 90 | 91 | readBytes, err := r.Read(buf[totalReadBytes:endOffset]) 92 | totalReadBytes += uint64(readBytes) 93 | 94 | if err == io.EOF { 95 | r.Close() 96 | break 97 | } else if err != nil { 98 | r.Close() 99 | return nil, fmt.Errorf("could not read content of file %s , err: %v", 100 | f.Name, err) 101 | } 102 | } 103 | 104 | elemsAsBytes := bytes.Split(buf[:totalReadBytes], []byte{44}) // 44 is a comma 105 | 106 | var result []uint64 107 | for _, elemBytes := range elemsAsBytes { 108 | elemStr := strings.TrimSpace(string(elemBytes)) 109 | 110 | e, err := strconv.ParseUint(elemStr, 10, 32) 111 | if err != nil { 112 | r.Close() 113 | return nil, fmt.Errorf("could not parse %s as uint32. Reading %s, err: %v", 114 | elemStr, f.Name, err) 115 | } 116 | result = append(result, e) 117 | } 118 | return result, nil 119 | } 120 | 121 | func benchmarkRealDataAggregate(b *testing.B, aggregator func(b []*Bitmap) int) { 122 | for _, dataset := range realDatasets { 123 | once := false 124 | b.Run(dataset, func(b *testing.B) { 125 | bitmaps, err := retrieveRealDataBitmaps(dataset, true) 126 | if err != nil { 127 | b.Fatal(err) 128 | } 129 | if once { 130 | c := aggregator(bitmaps) 131 | b.Logf("Dataset: %s Got cardinality: %d\n", dataset, c) 132 | once = false 133 | } 134 | b.ResetTimer() 135 | for i := 0; i < b.N; i++ { 136 | aggregator(bitmaps) 137 | } 138 | }) 139 | } 140 | } 141 | 142 | func BenchmarkRealDataFastOr(b *testing.B) { 143 | benchmarkRealDataAggregate(b, func(bitmaps []*Bitmap) int { 144 | return FastOr(bitmaps...).GetCardinality() 145 | }) 146 | } 147 | func BenchmarkRealDataFastParOr(b *testing.B) { 148 | benchmarkRealDataAggregate(b, func(bitmaps []*Bitmap) int { 149 | return FastParOr(4, bitmaps...).GetCardinality() 150 | }) 151 | } 152 | 153 | func BenchmarkRealDataFastAnd(b *testing.B) { 154 | benchmarkRealDataAggregate(b, func(bitmaps []*Bitmap) int { 155 | return FastAnd(bitmaps...).GetCardinality() 156 | }) 157 | } 158 | 159 | func TestOrRealData(t *testing.T) { 160 | test := func(t *testing.T, dataset string) { 161 | path, err := getDataSetPath(dataset) 162 | require.NoError(t, err) 163 | 164 | zipFile, err := zip.OpenReader(path) 165 | require.NoError(t, err) 166 | defer zipFile.Close() 167 | 168 | bitmaps := make([]*Bitmap, len(zipFile.File)) 169 | valMap := make(map[uint64]struct{}) 170 | 171 | res2 := NewBitmap() 172 | // For each file in the zip, create a new bitmap and check the created bitmap has correct 173 | // cardinality as well as it has all the elements. 174 | for i, f := range zipFile.File { 175 | vals, err := processZipFile(f) 176 | require.NoError(t, err) 177 | 178 | b := NewBitmap() 179 | for _, v := range vals { 180 | b.Set(v) 181 | res2.Set(v) 182 | valMap[v] = struct{}{} 183 | } 184 | require.Equal(t, len(vals), b.GetCardinality()) 185 | for _, v := range vals { 186 | require.True(t, b.Contains(v)) 187 | } 188 | bitmaps[i] = b 189 | } 190 | 191 | // Check that union operation is correct. 192 | res := FastOr(bitmaps...) 193 | 194 | t.Logf("Result: %s\n", res) 195 | require.Equal(t, len(valMap), res.GetCardinality()) 196 | require.Equal(t, len(valMap), res2.GetCardinality()) 197 | 198 | for k := range valMap { 199 | require.True(t, res.Contains(k)) 200 | require.True(t, res2.Contains(k)) 201 | } 202 | } 203 | 204 | for _, dataset := range realDatasets { 205 | t.Run(dataset, func(t *testing.T) { test(t, dataset) }) 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /setutil.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 by the roaring authors. 2 | // Licensed under the Apache License, Version 2.0. 3 | // Full version of the license is here: 4 | // https://github.com/RoaringBitmap/roaring/blob/master/LICENSE 5 | 6 | package sroar 7 | 8 | // TODO: Add license from roaring bitmap library. 9 | 10 | func min(a, b int) int { 11 | if a < b { 12 | return a 13 | } 14 | return b 15 | } 16 | func max(a, b int) int { 17 | if a > b { 18 | return a 19 | } 20 | return b 21 | } 22 | 23 | func equal(a, b []uint16) bool { 24 | if len(a) != len(b) { 25 | return false 26 | } 27 | for i := range a { 28 | if a[i] != b[i] { 29 | return false 30 | } 31 | } 32 | return true 33 | } 34 | 35 | func difference(set1 []uint16, set2 []uint16, buffer []uint16) int { 36 | if 0 == len(set2) { 37 | buffer = buffer[:len(set1)] 38 | for k := 0; k < len(set1); k++ { 39 | buffer[k] = set1[k] 40 | } 41 | return len(set1) 42 | } 43 | if 0 == len(set1) { 44 | return 0 45 | } 46 | pos := 0 47 | k1 := 0 48 | k2 := 0 49 | buffer = buffer[:cap(buffer)] 50 | s1 := set1[k1] 51 | s2 := set2[k2] 52 | for { 53 | if s1 < s2 { 54 | buffer[pos] = s1 55 | pos++ 56 | k1++ 57 | if k1 >= len(set1) { 58 | break 59 | } 60 | s1 = set1[k1] 61 | } else if s1 == s2 { 62 | k1++ 63 | k2++ 64 | if k1 >= len(set1) { 65 | break 66 | } 67 | s1 = set1[k1] 68 | if k2 >= len(set2) { 69 | for ; k1 < len(set1); k1++ { 70 | buffer[pos] = set1[k1] 71 | pos++ 72 | } 73 | break 74 | } 75 | s2 = set2[k2] 76 | } else { // if (val1>val2) 77 | k2++ 78 | if k2 >= len(set2) { 79 | for ; k1 < len(set1); k1++ { 80 | buffer[pos] = set1[k1] 81 | pos++ 82 | } 83 | break 84 | } 85 | s2 = set2[k2] 86 | } 87 | } 88 | return pos 89 | 90 | } 91 | 92 | func exclusiveUnion2by2(set1 []uint16, set2 []uint16, buffer []uint16) int { 93 | if 0 == len(set2) { 94 | buffer = buffer[:len(set1)] 95 | copy(buffer, set1[:]) 96 | return len(set1) 97 | } 98 | if 0 == len(set1) { 99 | buffer = buffer[:len(set2)] 100 | copy(buffer, set2[:]) 101 | return len(set2) 102 | } 103 | pos := 0 104 | k1 := 0 105 | k2 := 0 106 | s1 := set1[k1] 107 | s2 := set2[k2] 108 | buffer = buffer[:cap(buffer)] 109 | for { 110 | if s1 < s2 { 111 | buffer[pos] = s1 112 | pos++ 113 | k1++ 114 | if k1 >= len(set1) { 115 | for ; k2 < len(set2); k2++ { 116 | buffer[pos] = set2[k2] 117 | pos++ 118 | } 119 | break 120 | } 121 | s1 = set1[k1] 122 | } else if s1 == s2 { 123 | k1++ 124 | k2++ 125 | if k1 >= len(set1) { 126 | for ; k2 < len(set2); k2++ { 127 | buffer[pos] = set2[k2] 128 | pos++ 129 | } 130 | break 131 | } 132 | if k2 >= len(set2) { 133 | for ; k1 < len(set1); k1++ { 134 | buffer[pos] = set1[k1] 135 | pos++ 136 | } 137 | break 138 | } 139 | s1 = set1[k1] 140 | s2 = set2[k2] 141 | } else { // if (val1>val2) 142 | buffer[pos] = s2 143 | pos++ 144 | k2++ 145 | if k2 >= len(set2) { 146 | for ; k1 < len(set1); k1++ { 147 | buffer[pos] = set1[k1] 148 | pos++ 149 | } 150 | break 151 | } 152 | s2 = set2[k2] 153 | } 154 | } 155 | return pos 156 | } 157 | 158 | func union2by2Cardinality(set1 []uint16, set2 []uint16) int { 159 | pos := 0 160 | k1 := 0 161 | k2 := 0 162 | if 0 == len(set2) { 163 | return len(set1) 164 | } 165 | if 0 == len(set1) { 166 | return len(set2) 167 | } 168 | s1 := set1[k1] 169 | s2 := set2[k2] 170 | for { 171 | if s1 < s2 { 172 | pos++ 173 | k1++ 174 | if k1 >= len(set1) { 175 | pos += len(set2) - k2 176 | break 177 | } 178 | s1 = set1[k1] 179 | } else if s1 == s2 { 180 | pos++ 181 | k1++ 182 | k2++ 183 | if k1 >= len(set1) { 184 | pos += len(set2) - k2 185 | break 186 | } 187 | if k2 >= len(set2) { 188 | pos += len(set1) - k1 189 | break 190 | } 191 | s1 = set1[k1] 192 | s2 = set2[k2] 193 | } else { // if (set1[k1]>set2[k2]) 194 | pos++ 195 | k2++ 196 | if k2 >= len(set2) { 197 | pos += len(set1) - k1 198 | break 199 | } 200 | s2 = set2[k2] 201 | } 202 | } 203 | return pos 204 | } 205 | 206 | func intersection2by2( 207 | set1 []uint16, 208 | set2 []uint16, 209 | buffer []uint16) int { 210 | 211 | if len(set1)*64 < len(set2) { 212 | return onesidedgallopingintersect2by2(set1, set2, buffer) 213 | } else if len(set2)*64 < len(set1) { 214 | return onesidedgallopingintersect2by2(set2, set1, buffer) 215 | } else { 216 | return localintersect2by2(set1, set2, buffer) 217 | } 218 | } 219 | 220 | func intersection2by2Cardinality( 221 | set1 []uint16, 222 | set2 []uint16) int { 223 | 224 | if len(set1)*64 < len(set2) { 225 | return onesidedgallopingintersect2by2Cardinality(set1, set2) 226 | } else if len(set2)*64 < len(set1) { 227 | return onesidedgallopingintersect2by2Cardinality(set2, set1) 228 | } else { 229 | return localintersect2by2Cardinality(set1, set2) 230 | } 231 | } 232 | 233 | func intersects2by2( 234 | set1 []uint16, 235 | set2 []uint16) bool { 236 | // could be optimized if one set is much larger than the other one 237 | if (0 == len(set1)) || (0 == len(set2)) { 238 | return false 239 | } 240 | k1 := 0 241 | k2 := 0 242 | s1 := set1[k1] 243 | s2 := set2[k2] 244 | mainwhile: 245 | for { 246 | 247 | if s2 < s1 { 248 | for { 249 | k2++ 250 | if k2 == len(set2) { 251 | break mainwhile 252 | } 253 | s2 = set2[k2] 254 | if s2 >= s1 { 255 | break 256 | } 257 | } 258 | } 259 | if s1 < s2 { 260 | for { 261 | k1++ 262 | if k1 == len(set1) { 263 | break mainwhile 264 | } 265 | s1 = set1[k1] 266 | if s1 >= s2 { 267 | break 268 | } 269 | } 270 | 271 | } else { 272 | // (set2[k2] == set1[k1]) 273 | return true 274 | } 275 | } 276 | return false 277 | } 278 | 279 | func localintersect2by2( 280 | set1 []uint16, 281 | set2 []uint16, 282 | buffer []uint16) int { 283 | 284 | if (0 == len(set1)) || (0 == len(set2)) { 285 | return 0 286 | } 287 | k1 := 0 288 | k2 := 0 289 | pos := 0 290 | buffer = buffer[:cap(buffer)] 291 | s1 := set1[k1] 292 | s2 := set2[k2] 293 | mainwhile: 294 | for { 295 | if s2 < s1 { 296 | for { 297 | k2++ 298 | if k2 == len(set2) { 299 | break mainwhile 300 | } 301 | s2 = set2[k2] 302 | if s2 >= s1 { 303 | break 304 | } 305 | } 306 | } 307 | if s1 < s2 { 308 | for { 309 | k1++ 310 | if k1 == len(set1) { 311 | break mainwhile 312 | } 313 | s1 = set1[k1] 314 | if s1 >= s2 { 315 | break 316 | } 317 | } 318 | 319 | } else { 320 | // (set2[k2] == set1[k1]) 321 | buffer[pos] = s1 322 | pos++ 323 | k1++ 324 | if k1 == len(set1) { 325 | break 326 | } 327 | s1 = set1[k1] 328 | k2++ 329 | if k2 == len(set2) { 330 | break 331 | } 332 | s2 = set2[k2] 333 | } 334 | } 335 | return pos 336 | } 337 | 338 | func localintersect2by2Cardinality( 339 | set1 []uint16, 340 | set2 []uint16) int { 341 | 342 | if (0 == len(set1)) || (0 == len(set2)) { 343 | return 0 344 | } 345 | k1 := 0 346 | k2 := 0 347 | pos := 0 348 | s1 := set1[k1] 349 | s2 := set2[k2] 350 | mainwhile: 351 | for { 352 | if s2 < s1 { 353 | for { 354 | k2++ 355 | if k2 == len(set2) { 356 | break mainwhile 357 | } 358 | s2 = set2[k2] 359 | if s2 >= s1 { 360 | break 361 | } 362 | } 363 | } 364 | if s1 < s2 { 365 | for { 366 | k1++ 367 | if k1 == len(set1) { 368 | break mainwhile 369 | } 370 | s1 = set1[k1] 371 | if s1 >= s2 { 372 | break 373 | } 374 | } 375 | 376 | } else { 377 | // (set2[k2] == set1[k1]) 378 | pos++ 379 | k1++ 380 | if k1 == len(set1) { 381 | break 382 | } 383 | s1 = set1[k1] 384 | k2++ 385 | if k2 == len(set2) { 386 | break 387 | } 388 | s2 = set2[k2] 389 | } 390 | } 391 | return pos 392 | } 393 | 394 | func advanceUntil( 395 | array []uint16, 396 | pos int, 397 | length int, 398 | min uint16) int { 399 | lower := pos + 1 400 | 401 | if lower >= length || array[lower] >= min { 402 | return lower 403 | } 404 | 405 | spansize := 1 406 | 407 | for lower+spansize < length && array[lower+spansize] < min { 408 | spansize *= 2 409 | } 410 | var upper int 411 | if lower+spansize < length { 412 | upper = lower + spansize 413 | } else { 414 | upper = length - 1 415 | } 416 | 417 | if array[upper] == min { 418 | return upper 419 | } 420 | 421 | if array[upper] < min { 422 | // means 423 | // array 424 | // has no 425 | // item 426 | // >= min 427 | // pos = array.length; 428 | return length 429 | } 430 | 431 | // we know that the next-smallest span was too small 432 | lower += (spansize >> 1) 433 | 434 | mid := 0 435 | for lower+1 != upper { 436 | mid = (lower + upper) >> 1 437 | if array[mid] == min { 438 | return mid 439 | } else if array[mid] < min { 440 | lower = mid 441 | } else { 442 | upper = mid 443 | } 444 | } 445 | return upper 446 | 447 | } 448 | 449 | func onesidedgallopingintersect2by2( 450 | smallset []uint16, 451 | largeset []uint16, 452 | buffer []uint16) int { 453 | 454 | if 0 == len(smallset) { 455 | return 0 456 | } 457 | buffer = buffer[:cap(buffer)] 458 | k1 := 0 459 | k2 := 0 460 | pos := 0 461 | s1 := largeset[k1] 462 | s2 := smallset[k2] 463 | mainwhile: 464 | 465 | for { 466 | if s1 < s2 { 467 | k1 = advanceUntil(largeset, k1, len(largeset), s2) 468 | if k1 == len(largeset) { 469 | break mainwhile 470 | } 471 | s1 = largeset[k1] 472 | } 473 | if s2 < s1 { 474 | k2++ 475 | if k2 == len(smallset) { 476 | break mainwhile 477 | } 478 | s2 = smallset[k2] 479 | } else { 480 | 481 | buffer[pos] = s2 482 | pos++ 483 | k2++ 484 | if k2 == len(smallset) { 485 | break 486 | } 487 | s2 = smallset[k2] 488 | k1 = advanceUntil(largeset, k1, len(largeset), s2) 489 | if k1 == len(largeset) { 490 | break mainwhile 491 | } 492 | s1 = largeset[k1] 493 | } 494 | 495 | } 496 | return pos 497 | } 498 | 499 | func onesidedgallopingintersect2by2Cardinality( 500 | smallset []uint16, 501 | largeset []uint16) int { 502 | 503 | if 0 == len(smallset) { 504 | return 0 505 | } 506 | k1 := 0 507 | k2 := 0 508 | pos := 0 509 | s1 := largeset[k1] 510 | s2 := smallset[k2] 511 | mainwhile: 512 | 513 | for { 514 | if s1 < s2 { 515 | k1 = advanceUntil(largeset, k1, len(largeset), s2) 516 | if k1 == len(largeset) { 517 | break mainwhile 518 | } 519 | s1 = largeset[k1] 520 | } 521 | if s2 < s1 { 522 | k2++ 523 | if k2 == len(smallset) { 524 | break mainwhile 525 | } 526 | s2 = smallset[k2] 527 | } else { 528 | 529 | pos++ 530 | k2++ 531 | if k2 == len(smallset) { 532 | break 533 | } 534 | s2 = smallset[k2] 535 | k1 = advanceUntil(largeset, k1, len(largeset), s2) 536 | if k1 == len(largeset) { 537 | break mainwhile 538 | } 539 | s1 = largeset[k1] 540 | } 541 | 542 | } 543 | return pos 544 | } 545 | 546 | func binarySearch(array []uint16, ikey uint16) int { 547 | low := 0 548 | high := len(array) - 1 549 | for low+16 <= high { 550 | middleIndex := int(uint32(low+high) >> 1) 551 | middleValue := array[middleIndex] 552 | if middleValue < ikey { 553 | low = middleIndex + 1 554 | } else if middleValue > ikey { 555 | high = middleIndex - 1 556 | } else { 557 | return middleIndex 558 | } 559 | } 560 | for ; low <= high; low++ { 561 | val := array[low] 562 | if val >= ikey { 563 | if val == ikey { 564 | return low 565 | } 566 | break 567 | } 568 | } 569 | return -(low + 1) 570 | } 571 | 572 | func union2by2(set1 []uint16, set2 []uint16, buffer []uint16) int { 573 | pos := 0 574 | k1 := 0 575 | k2 := 0 576 | if 0 == len(set2) { 577 | buffer = buffer[:len(set1)] 578 | copy(buffer, set1[:]) 579 | return len(set1) 580 | } 581 | if 0 == len(set1) { 582 | buffer = buffer[:len(set2)] 583 | copy(buffer, set2[:]) 584 | return len(set2) 585 | } 586 | s1 := set1[k1] 587 | s2 := set2[k2] 588 | buffer = buffer[:cap(buffer)] 589 | for { 590 | if s1 < s2 { 591 | buffer[pos] = s1 592 | pos++ 593 | k1++ 594 | if k1 >= len(set1) { 595 | copy(buffer[pos:], set2[k2:]) 596 | pos += len(set2) - k2 597 | break 598 | } 599 | s1 = set1[k1] 600 | } else if s1 == s2 { 601 | buffer[pos] = s1 602 | pos++ 603 | k1++ 604 | k2++ 605 | if k1 >= len(set1) { 606 | copy(buffer[pos:], set2[k2:]) 607 | pos += len(set2) - k2 608 | break 609 | } 610 | if k2 >= len(set2) { 611 | copy(buffer[pos:], set1[k1:]) 612 | pos += len(set1) - k1 613 | break 614 | } 615 | s1 = set1[k1] 616 | s2 = set2[k2] 617 | } else { // if (set1[k1]>set2[k2]) 618 | buffer[pos] = s2 619 | pos++ 620 | k2++ 621 | if k2 >= len(set2) { 622 | copy(buffer[pos:], set1[k1:]) 623 | pos += len(set1) - k1 624 | break 625 | } 626 | s2 = set2[k2] 627 | } 628 | } 629 | return pos 630 | } 631 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Dgraph Labs, Inc. and Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package sroar 18 | 19 | import ( 20 | "log" 21 | "math" 22 | "reflect" 23 | "unsafe" 24 | 25 | "github.com/pkg/errors" 26 | ) 27 | 28 | func assert(b bool) { 29 | if !b { 30 | log.Fatalf("%+v", errors.Errorf("Assertion failure")) 31 | } 32 | } 33 | func check(err error) { 34 | if err != nil { 35 | log.Fatalf("%+v", err) 36 | } 37 | } 38 | func check2(_ interface{}, err error) { 39 | check(err) 40 | } 41 | 42 | func min16(a, b uint16) uint16 { 43 | if a < b { 44 | return a 45 | } 46 | return b 47 | } 48 | func max16(a, b uint16) uint16 { 49 | if a > b { 50 | return a 51 | } 52 | return b 53 | } 54 | 55 | // Returns sum of a and b. If the result overflows uint64, it returns math.MaxUint64. 56 | func addUint64(a, b uint64) uint64 { 57 | if a > math.MaxUint64-b { 58 | return math.MaxUint64 59 | } 60 | return a + b 61 | } 62 | 63 | func toByteSlice(b []uint16) []byte { 64 | // reference: https://go101.org/article/unsafe.html 65 | var bs []byte 66 | hdr := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) 67 | hdr.Len = len(b) * 2 68 | hdr.Cap = hdr.Len 69 | hdr.Data = uintptr(unsafe.Pointer(&b[0])) 70 | return bs 71 | } 72 | 73 | // These methods (byteSliceAsUint16Slice,...) do not make copies, 74 | // they are pointer-based (unsafe). The caller is responsible to 75 | // ensure that the input slice does not get garbage collected, deleted 76 | // or modified while you hold the returned slince. 77 | //// 78 | func toUint16Slice(b []byte) (result []uint16) { 79 | var u16s []uint16 80 | hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u16s)) 81 | hdr.Len = len(b) / 2 82 | hdr.Cap = hdr.Len 83 | hdr.Data = uintptr(unsafe.Pointer(&b[0])) 84 | return u16s 85 | } 86 | 87 | // BytesToU32Slice converts the given byte slice to uint32 slice 88 | func toUint64Slice(b []uint16) []uint64 { 89 | var u64s []uint64 90 | hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u64s)) 91 | hdr.Len = len(b) / 4 92 | hdr.Cap = hdr.Len 93 | hdr.Data = uintptr(unsafe.Pointer(&b[0])) 94 | return u64s 95 | } 96 | 97 | //go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers 98 | func memclrNoHeapPointers(p unsafe.Pointer, n uintptr) 99 | 100 | func Memclr(b []uint16) { 101 | if len(b) == 0 { 102 | return 103 | } 104 | p := unsafe.Pointer(&b[0]) 105 | memclrNoHeapPointers(p, uintptr(len(b))) 106 | } 107 | --------------------------------------------------------------------------------