From d2336903121f424a0ad3875fc148e7b681ad434c Mon Sep 17 00:00:00 2001 From: Alexandr Romanenko Date: Tue, 26 Sep 2023 02:31:01 +0300 Subject: [PATCH 1/4] fix(cubestore): Wrong pre-aggregations count distinct approx count when using PostgreSQL database --- rust/cubestore/cubehll/src/instance.rs | 101 ++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/rust/cubestore/cubehll/src/instance.rs b/rust/cubestore/cubehll/src/instance.rs index 3cc45fb5b9cad..c0e6c09a5f891 100644 --- a/rust/cubestore/cubehll/src/instance.rs +++ b/rust/cubestore/cubehll/src/instance.rs @@ -174,16 +174,53 @@ impl HllInstance { num_hashes ))); } - // Convert to sparse representation in Airlift. + // Convert to sparse representation in Airlift. By analogy with the code from postgress hll: + // TODO For now we read hll-storage-spec only for postgress. For other realizations + // this algorithm can differ. + /* + uint64_t mask = nregs - 1; + + size_t maxregval = (1 << nbits) - 1; + + size_t ndx = elem & mask; + + uint64_t ss_val = elem >> log2nregs; + + size_t p_w = ss_val == 0 ? 0 : __builtin_ctzll(ss_val) + 1; + + Assert(ndx < msc_regs_idx_limit()); + + if (p_w > maxregval) + p_w = maxregval; + + if (mscp->msc_regs[ndx] < p_w) + mscp->msc_regs[ndx] = p_w; + * */ + let mask = num_buckets as u64 - 1; let mut indices = Vec::with_capacity(num_hashes); let mut values = Vec::with_capacity(num_hashes); let mut data = Cursor::new(data); + let maxval = (1 << reg_width) as u32 - 1; while !data.is_empty() { let hash = data.read_u64::().unwrap(); - indices.push(compute_index(hash, log_num_buckets)); - values.push(compute_value(hash, log_num_buckets)); + let ind = hash & mask; + let val = hash >> log_num_buckets; + let zeroes = if val == 0 { + 0 + } else { + val.trailing_zeros() + 1 + }; + + let zeroes = if zeroes > maxval { + maxval + } else { + zeroes + }; + indices.push(ind as u32); + values.push(zeroes as u8); } + return Ok(HllInstance::Sparse(SparseHll::new_from_indices_and_values( log_num_buckets, indices, @@ -578,6 +615,7 @@ impl DenseHll { }; } + pub fn new_from_entries(index_bit_len: u8, values: Vec) -> Result { DenseHll::is_valid_bit_len(index_bit_len)?; let num_buckets = number_of_buckets(index_bit_len); @@ -1370,7 +1408,7 @@ mod tests { sparse.each_bucket(|_, value| { lz_counts.push(value); }); - assert_eq!(lz_counts, vec![4]); + assert_eq!(lz_counts, vec![5]); // Sparse encoding, 169 values. // TODO: the estimate is off, fix calculation in sparse mode. @@ -1402,6 +1440,61 @@ mod tests { let h = read("148b7f21083288a4320a12086719c65108c1088422884511063388232904418c8520484184862886528c65198832106328c83114e6214831108518d03208851948511884188441908119083388661842818c43190c320ce4210a50948221083084a421c8328c632104221c4120d01284e20902318ca5214641942319101294641906228483184e128c43188e308882204a538c8328903288642102220c64094631086330c832106320c46118443886329062118a230c63108a320c23204a11852419c6528c85210a318c6308c41088842086308ce7110a418864190650884210ca631064108642a1022186518c8509862109020a0a4318671144150842400e5090631a0811848320c821888120c81114a220880290622906310d0220c83090a118c433106128c221902210cc23106029044114841104409862190c43188111063104c310c6728c8618c62290441102310c23214440882438ca2110a32908548c432110329462188a43946328842114640944320884190c928c442084228863318a2190a318c6618ca3114651886618c44190c5108e2110612144319062284641908428882314862106419883310421988619ca420cc511442104633888218c4428465288651910730c81118821088218c6418c45108452106519ce410d841904218863308622086211483198c710c83104a328c620906218864118623086418c8711423094632186420c4620c41104620a441108e40882628c6311c212046428c8319021104672888428ca320c431984418c4209043084451886510c641108310c4c20c66188472146310ca71084820c621946218c8228822190e2410861904411c27288621144328c6440c6311063190813086228ca710c2218c4718865188c2114850888608864404a3194e22882310ce53088619ca31904519503188e1118c4214cb2948110c6119c2818c843108520c43188c5204821186528c871908311086214c630c4218c8418cc3298a31888210c63110a121042198622886531082098c419c4210c6210c8338c25294610944518c442104610884104424206310c8311462288873102308c2440c451082228824310440982220c4240c622084310c642850118c641148430d0128c8228c2120c221884428863208c21a0a4190a4404c21186548865204633906308ca32086211c8319ce22146520c6120803318a518c840084519461208c21908538cc428c2110844384e40906320c44014a3204e62042408c8328c632146318c812004310c41318e3208a5308a511827104a4188c51048421446090a7088631102231484104473084318c41210860906919083190652906129c4628c45310652848221443114420084500865184a618c81198c32906418c63190e320c231882728484184671888309465188a320c83208632144318c6331c642988108c61218812144328d022844021022184a31908328c6218c2328c4528cc541428190641046418c84108443146230c6419483214232184411863290a210824318c220868194631106618c43188821048230c4128c6310c0330462094241106330c42188c321043118863046438823110a041464108e3190e4209a11902439c43188631104321008090441106218c6419064294a229463594622244320cc71184510902924421908218c62308641044328ca328882111012884120ca52882428c62184442086718c4221c8211082208a321023115270086218c4218c6528ce400482310a520c43104a520c44210811884118c4310864198263942331822").unwrap(); assert_eq!(h.cardinality(), 9722); } + + #[test] + fn test_hll_postgr_test() { + + + let t1 = "Eot/HUOjtjMdhpk="; + let t2 = "FIt/OVKSkKYhSlQQhClKcpXEIRBEEKZJCHKlRjGKQhSlMwx0HQVRUFIdJkEOc5iEKY6TnIZJCFMRBUESgyimIhSDIKUxzGIgyCmWYpikIYxjHKVRknOVJzGMUyzoKUqTmKcpSIWQpTGMQ5CmQcxkEIgqiISoxzmWgpylQUxirQUhzGQkxDGQhJkjOhRyHKZBDlQRRjMSUhCmIVBTFKQqDGOcxUFOQpTlKkxSnMgqkJOU5iIKYxSGOY5TIQVRTGIMpiHOkozFKVRSlKgiSFIUpjlIchilQcpylGYZkIMghzHOgpzGQkpylIYhklMYpSFMYxTFKcxzHKkhyGOkpTmSQpjLcYpkFKhaCFMY6BnKYpknKdBUEOUxilMdBioMVCCIOYpzmSkxkGIgxSmQYpyqOcxjIKUpEHMc5VGMopzmKgxjJKYpiEGghjGKZRUFQQyTEMc5zGQcx0GGYhCqKQ6UnKc5inOUhUFIcpzlMUyEHOQxkJWUxkJQlCTlYRCymKchVJOdBzFQY5TlMUhiHMVJSlKUpkGIopSnUUhjFQcpjKQQpjoMYxDKKkxDpSYqCHUZJToQcijrIchTjI0x0GMciCKUghUJKkxTIQhJjGOYylHMwxzlIlSCnOkqCJKcpllOQiVHKZAzpOYhkHMU6zFGYpylKhRznKQxCGIQ5DFMYxmkKQxTFKYxznQUxCJMZRiLKUpzHQUyCoKVCTKKYpSkKg6UHIlBSmQYxTlQYqDIIdJimKY5zIKYpiGMcxylOkqCFQVJylOcxSmKkpjIIZRWEKUpTGIY5xsMdB0HKk6BjGYxjFKQxjmQZBSlMYhEpMlRSHKU5TmKQhjIKYxyEMU6DmMcpkFOgxEEKQpToScxxkMVBCIKVCVGOY5UqQYxikKUxCKSY5CnWQ6TlIg6TFKURTGKZCUJSlRRqKZJ2EOgiSESMpDmQQpyISRCDmOopilIQpSIeUo0lMYiSGKcpynQUhDkKRBDoOVpTIIU5SGKUhjIKcxTHIYxGEQY5UkIUhjHQUxDoGUiTlOUhzmGg6imOY5ilKUpCFQZBjGKYhjEMUhRqMUyCnMQxTIQkyTGMkwzHQg5RmKQiTJKQ50FORpypMVBxlKRBTLMcpEGMZBjlMg5TpMUqkpQkxjHScqjlUgqSmOVJlHIYxhmIQyEGWkxyEOMpTEKY5SoQZhjHQUpzGWVChlKghymOZhDmQYpEnQcpDnOUpTGGY5jlMc5TkKUxzFOcxSmIkpToQU5DoMgyhmIhRimIYhykKU5jmQk6FHcUpjJIc5TFIU5UoIUwzGKUhjJScpilMhJDFKNBjHKkpUHMYhkHQNBUEQpSDHOghhkKYpRmKYpzGOYxjFIYpjleY53FGU5SmKYpiJMRDjlMpBiHKgyhoMU5iHUUqEKOg5CJOUg0GOcpiESghDkac6UESUyTHSUy0FOgqDGUU5TGMUyTHKUpkGMdBzpGlBCFMU5TmMY5UGQYyEmKQxCnMRZyGKYqDJKZBilOUx0HIZBTFKMxiJSg5TEYQxUJOkxDlOYZ0EKRCTHOUxknQQh1FKQpSIOdBUnMVQzHKQ5imWsxhlQpBSoKZCDMIk5zQMUpilOMxUGIUxiFMU6THOUplEIYxyFORR1mKZJTFKUoyrIYhTFKY6DmOkiEFKZJEKMYhitIgyyGKUxjkKgxTmMZBCjKQpDmScpjkQchyFQYhinQhIzKMQ="; + let t3 = "FIt/OVKSkKYhSlQQhClKcpXEIRBEEKZJCHKlRjGKQhSlMwx0HQVRUFIdJkEOc5iEKY6TnIZJCFMRBUESgyimIhSDIKUxzGIgyCmWYpikIYxjHKVRknOVJzGMUyzoKUqTmKcpSIWQpTGMQ5CmQcxkEIgqiISoxzmWgpylQUxirQUhzGQkxDGQhJkjOhRyHKZBDlQRRjMSUhCmIVBTFKQqDGOcxUFOQpTlKkxSnMgqkJOU5iIKYxSGOY5TIQVRTGIMpiHOkozFKVRSlKgiSFIUpjlIchilQcpylGYZkIMghzHOgpzGQkpylIYhklMYpSFMYxTFKcxzHKkhyGOkpTmSQpjLcYpkFKhaCFMY6BnKYpknKdBUEOUxilMdBioMVCCIOYpzmSkxkGIgxSmQYpyqOcxjIKUpEHMc5VGMopzmKgxjJKYpiEGghjGKZRUFQQyTEMc5zGQcx0GGYhCqKQ6UnKc5inOUhUFIcpzlMUyEHOQxkJWUxkJQlCTlYRCymKchVJOdBzFQY5TlMUhiHMVJSlKUpkGIopSnUUhjFQcpjKQQpjoMYxDKKkxDpSYqCHUZJToQcijrIchTjI0x0GMciCKUghUJKkxTIQhJjGOYylHMwxzlIlSCnOkqCJKcpllOQiVHKZAzpOYhkHMU6zFGYpylKhRznKQxCGIQ5DFMYxmkKQxTFKYxznQUxCJMZRiLKUpzHQUyCoKVCTKKYpSkKg6UHIlBSmQYxTlQYqDIIdJimKY5zIKYpiGMcxylOkqCFQVJylOcxSmKkpjIIZRWEKUpTGIY5xsMdB0HKk6BjGYxjFKQxjmQZBSlMYhEpMlRSHKU5TmKQhjIKYxyEMU6DmMcpkFOgxEEKQpToScxxkMVBCIKVCVGOY5UqQYxikKUxCKSY5CnWQ6TlIg6TFKURTGKZCUJSlRRqKZJ2EOgiSESMpDmQQpyISRCDmOopilIQpSIeUo0lMYiSGKcpynQUhDkKRBDoOVpTIIU5SGKUhjIKcxTHIYxGEQY5UkIUhjHQUxDoGUiTlOUhzmGg6imOY5ilKUpCFQZBjGKYhjEMUhRqMUyCnMQxTIQkyTGMkwzHQg5RmKQiTJKQ50FORpypMVBxlKRBTLMcpEGMZBjlMg5TpMUqkpQkxjHScqjlUgqSmOVJlHIYxhmIQyEGWkxyEOMpTEKY5SoQZhjHQUpzGWVChlKghymOZhDmQYpEnQcpDnOUpTGGY5jlMc5TkKUxzFOcxSmIkpToQU5DoMgyhmIhRimIYhykKU5jmQk6FHcUpjJIc5TFIU5UoIUwzGKUhjJScpilMhJDFKNBjHKkpUHMYhkHQNBUEQpSDHOghhkKYpRmKYpzGOYxjFIYpjleY53FGU5SmKYpiJMRDjlMpBiHKgyhoMU5iHUUqEKOg5CJOUhUGOcpiESghDkac6UESUyTHSUy0FOgqDGUU5TGMUyTHKUpkGMdBzpGlBCFMU5TmMY5UGQYyEmKQxCnMRZyGKYqDJKZBilOUx0HIZBTFKMxiJSg5TEYQxUJOkxDlOYZ0EKRCTHOUxknQQh1FKQpSIOdBUnMVQzHKQ5imWsxhlQpBSoKZCDMIk5zQMUpilOMxUGIUxiFMU6THOUplEIYxyFORR1mKZJTFKUoyrIYhTFKY6DmOkiEFKZJEKMYhitIgyyGKUxjkKgxTmMZBCjKQpDmScpjkQchyFQYhinQhIzKMQ="; + let s1 = base64::decode(t1).unwrap(); + let s2 = base64::decode(t2).unwrap(); + let s3 = base64::decode(t3).unwrap(); + + let h1 = HllInstance::read_hll_storage_spec(&s1).unwrap(); + let mut h2 = HllInstance::read_hll_storage_spec(&s2).unwrap(); + let h3 = HllInstance::read_hll_storage_spec(&s3).unwrap(); + h2.merge_with(&h1); + assert_eq!(h2.cardinality(), h3.cardinality()); + + let data = vec![ + "FIt/GEZDGAQSBBCYxBkCIgRkAIJwjYMoQjOQJgDEEgBFIIQyjKMIhiGEJSACQgQhEcRAiGMARgGEwhDOMABhCIIhACUQjDEQgCDKIRDDCQYRECAYjDAIJhGIcIhCKBAzCGRBhhEEYhDEMghgIYYRiGEIzFMIIhCGAhxBGQYiGCQZTkCIwQhIIAxDGUJCDIMAhDGI4QjGQIkDCJBCCEIYhBGUZAiKIBhEQMQxhIQYwCIEIiAEMhBCGMAQkGMJTHCEIAiEMZBkCEoglMEZRgEYQyDOQgxhAMIhnGEQlCIEZADEEQgiIgYhEIAgwiCIQRCEIAhiEEggiCMQRCIIojBIEQhlEIhBGEMYkjEEIAlCMZRjGIYkjCIAzDGMIwnGAYiBGEoinCYoBBIIIxhKEQxCGEIRhGQIRlAkYihIAQxIIIQwhIMAhiEUhRCAQYRBEIAhCIcZggEMZCBSQYhkIEoAiEYZBiEEQSDMIQxDGQgxCEIoCCAkJhkIMQCCEIQRCSMghiCUICgAEYgkMYAgiGYYQFAERCBEMBglCMxBBIMIAiKEIykGMoRCIMRTFCUQxkOMRQhGUg0hIMAxDAUghjIMQSFCE5AjEIYiCEUZBCGMQxACYIAjGMIhlKYYxCIQYBiIEgTCEMJAhGEoBjAUYSkEIRRCOMIBgAMZACEIZRiCIhRkAIJACEMZAiEIYyjCERABCUAwBAMARhEQKBCEMZiiGEQhBCQ4hDIQQQkCIQzCMcTBlEMRhCCIZDFEARQhKIgjjAIRRkEIYyCEIwwDAQgiCKMZQFCIYiECIgwECUIRFOMYRgEMQSAEMQyBCQgQhAAQzgEIQSAGEQQACMYSAAIIwmIIIhiCMYAiEAYhAIcYSiCMYhiCEIQCKIJAiGMwRjCQgxCIEYQDEMoShKEYRiEAYgiGYgQgCMRhiAMQCBAUoTiKIZyAGMZRBEIZSAOcghGKcYwgCIohCCMowCOQYjCCYQgkGMRADGIghBCQQwlIEYhDIEAxBEQYRHKcgRAEYQQDCMRBhKEYTECQIgCGEQwEKEgxkMEYiiGQIxDEMYRGAFJhDQIYgjGIxBjEUgiDKIJhjEEQwiGIhSEEMICHEIpRCEI4xBEUIxjGQIxBCIQlrEQQigAEQQjMEgACCIQhCIIIjCGMoxkEIQBhIQCBCYIggiEIIhDIMoiCEI4wlGUhBBGYQwjCIIiEKQQQhCEABBEEZABKIoAjGcIRgCIoxjEIYyECQoxjEEYhnKMJRlIgYyjEEYhkMAIQDCIIRCEUZBCGIAyCEQAxCGEARiKAIgkEUIRjEEQhkIUIQjKEYwiAJARACIghDCIgAnIBATCKUgAjCYghFKQYBGEUZSiGIZRDEMgSBCMIRDEEIglGRBCiCEQhiGUIxCKIhhBCUJBDIIgiEEcBQmGMZBiKIQjBIUhyCAMYTAEEJhEKcAhlGMIyBEMgQiEIJRCGUYRBCEIhDMMpBAIEYwjEgIRnEEogDIQKyhOIxhhCAYhhEIYRKIIAiBGIAxEOMQgiGEoikEEIzBIIYQmGUghCEYg0HGIRDjGYYihAEYSFEMoxjEUIyDCQAyiAMQBhGIA0AOEwiCCIRiBGEohCEkpSFEYJRjCEYSCGUwikEMgijCEYhEIIJxhCMQhAGEQSiCEQyiEQBBjGUYxFKYIxCCIYhCIUISFCIJBBCMRSjEMYglEIAhCCMohBCMQgBEA=", + "Eot/N/V0+Dra68JKY2b5infzYg==", + "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VKTQ0+8JUi/y9UAvEhi6MIULIna4vtI0k4ecVxKq2TXbTSiZOrW5NzxbjpEd95g3PKo6TFL7YzwWvouRWt9BPUGcqS1E/kREJunJpa2GRURgwsWM2WdRR7CwhyVwtKRIvMkiWTNKhUoDie9b6cNeSrTSznBVRRFNB6JDcJkTEU0SqSr4hdYvThh7k3ka53BQ76CXpCLJhlJ5mWdlKCSuWF8/2vbIJXde/+y3r9fzJl/EDyqKK0GXYKVukuFerQVirfQ40siHFWc3ib9uql07Z9MaFaFp3VZtk4UfWh1uhG7dJCBNzMhnbz1NwNYfiyRv0KD8hPz+0W/i6KxOH28hcLKkbI3B5E9wz2PdVyWupHE6ODhjFnBMcptBOcuSeMV2G4U71cAYfXiHZHoyMliHfSjmgFilk40=", + "E4t/AAEBIwHjAkICYwKBAuEDAgNBA8EEhATBB4EIQwiBCKUI4QmCCaEKIwphCuELRwtkDOINAQ4BDkMPohAhESMSoRMGEyEUAxUBFWEWAhZDFuMXBRdBF8MYYRiCGeIaQhrjHEEcghzDHeEeAx5CHwEgQyHDImEigSLDI0IlQidBJ2MnoimEKgIrISyiLqEvYTCBMYEyATKiMyIzwTSBNeQ2BTZhN0M3YTeBOCE4QTjhOQE64TsBPCE9Yj4hPoI+oT8DP8Q/4UBhQIFAwkECQqFFYUWCRiRGQUdBSuFMok2iTuZPZVAEUONRQlGhUmFSgVTJVgJWJVfBWERZAVsBW6NcoV0BXcJfIWEiYeJjYWXDZsFnoWhCaOFpImmBaeFqo2wFbOFtAm4LbqFu4m8jb2ZwInFjcgFyg3OCc6V1QXWCdaF2pXbCduF3YXfDeAN4QXkheiJ7Y31ifaR94n7Bf4KA4YKhg2GEgYSmheGGIoZDhsWIIYkCiUGJwonhiiGKoYsii6SNAY1CjgKPwZCjkWGRwpHikkGSxpNClcGWoZbCl0OXg5ehmIGaA5qhmuObBZzCnUGfIaDCoSKiJKJDo8GkoqVhpaKmQqdhqCKpgauBrQGtI63CryOvgq+isAGwgrDBsaOzQbRjtOG1IbXhtyK4ZLiluWG6JLrhvWW+Yb8CwSHDIcSBxUHFosZhxwPHgsilyWTJwspiyqjLIcuBzGLMxM0FzeTOJM+B0ATSIdMk04HT4tUB1aHXwtlh2iPaQ9rh3QLdId1j3aPfI+IB4iLiY+KB4qXjQ+Ph5QLlQ+Xl5qLnAuci54HnyOkC6WHpweoB6qProuwh7KLtY+4h7mHuoe7k7yLvYu+h8AHyQfLk82PzofSB9aH14/oh+qP7A/wk/KL8xv0h/cH+Qf6H", + "E4t/AAEAgQHjAiICYQLhAwIEYgTBBWMGQQeDB+UIAQiDCKUIwQjhCYUKBAojCmMK4gtmDEEM4g0hDWINgw3iDgEOIQ6BD6IP4xAhEEEQYhDEEQIRIxKFEqETghOiE+EUARRBFQEVIRVCFcEWAxbCFuMXgRghGIIYoRrjGwIbQhvhHAEcIR2hHeEeAx5BHuEfYR+hICEgQyDCISIhgSLhI2Ej4iRhJIElIyVCJgImgibCJ2MnwSfkKYQpwyoBKkEqYishK4IsoizGLWEvQi+FMIEwoTDBMSExQTGBNEE0YTUBNUM15DZhNsI3RTgBOCE4RjihOWM6IjphOoI6oTtiO8E8ITyCPKM85D1iPYM9wT3mPoI/CT+DP+FAoUECQgNDgUPCQ+FEgUWCRiRG4UchR0JH4UhBSMFKJEqhSyFLQUvhTCFMxU2iTmFO5k/CUCFQQVCBUKRQwlDjUSRRQlIEUmFShVKhUwVTxFQhVGFUyVUCVUNVYVWiVgJW4VciV2FXwVgHWERYg1ikWQJaIlpjWuFbA1siXKVdAV1CXWJegV8BXyNfQV+jYGZgwWJBYuFjJGPBY+NkAmWBZcJmRWbBZwJnYmekZ8Vn42gBaCFoQmlEaqNsQmzIbQJtzW4BbiNug3BCcGNwwXEBcSNx4XIBcsVzIXOCc8F1RnVidcN2YXaDd6R3w3lBeYF5onnjeiJ6gXrhewR7Y3ume8F8AnwjfGF8gXzCfYN+gX7BfyGBYoIigkGCwYOBg6KEIYSChSSFwYZhhoSGxYiCiUGJwYohi8SMIYyljMKNQo6hjuGPA5ABkKGQxJFBkcGSoZTBlOKVIZWhlgGWwpdDmAGYgZmBmgGaRJqBmuObBZthm6GcoZzCnoOfAp+ooCOgQqBhoQGhgaGjo8GkYqSBpQKlQaWnpeKmIqcip2GoIahkqKGowqjiqYepoqnFqeGrgavhrGGsoazBrOKtAa0jrkGuwa9Dr4KvxbAhsMKxIbGjsgKzJLNBs4G0ArSBtMG1A7ZhtoK2wbcit4K4AbiBuKO5A7lhuYK6JLpjuoK6orrIuuG7gbxhvWG+Yb8Cv0K/or/owAXAw8EBwSLBocIBwkLCgcKiwwHDIcWixiLGpMcDx2PIAcglyIPIpclkyeLKqMtBy2TLgcxhzILM4c0izYHOJM8BzyPPgc/D0AHRAdFB0eHSAtOB0+LUpdTC1QHVJNXi1oLW4dcG18LX5NgB2CTY4doD2iHaY9th2+PcAdyi3ULdo93B3gHeQd6h3uPfAt+h4CHhBOEh4ULhYeIC4iLiY+Kh4uPjAeNC5ITkweUh5aHmIeZB5qLngeiC6MLpQemB6cHqBeoj6qPqw+th6+HsAuwh7ILswe0B7WPtoe4m7oHuwe8h72LwgfEG8ULyQ/Lh8wHzIvSB9YL1pvXj9qL3Zfgh+GH4gfoC+iH6ofsD++H8JPxi/KL9Av2j/kH+gf8C/2H/wQ==", + "E4t/AAEAoQDCAWEB4wInAmEC4wNmA6MEIQSBBMEFIwWCBaEFwwYCBkIHgQemB8EH4QhECKEIwgkiCYIJxAnhCiMKpAsBCyML4QxBDOINAQ7DDuEPohAhEGIQohDBEOERAhEjEWER5RKhFIEUohUDFSEWQxhEGOEZQRmiGkIaoRrCGuMbQxzjHQUdQx2CHcQd4R5CIEMhIiFmI2Ej4iTkJaMlxCYiJqEnYyfkKIoowSjhKWIphioiKkEqYSrCKyErRyuBK+IsAS2hLeEuYi9CL4EwAjBhMIExAzEkMWExojJCMqIywzNhNIE1RjVhNYE15DYFNmE2wjcBN0U3oTfCN+M4QTjkOaI54zohO2I8IT1jPcE/4UCBQQNBxUKjQyNDgUPCQ+JEAkTBRORFJUXBRiRGpkcBR8FIQUhhSQRKJEphSwFLIUuBTCFMYk0CTWJNgk2iTmFOwU7mTwJQIVBBUKRQ41EhUUJSAVIhUqNTAlRhVINUyVUBVgJWRFdhWIFYpFkBWSFZQVnCWqFbIVuiW8FdQl4EXoFeol+CX8Jf4mCiYMNhQmPBZUJlgWWiZgNmoWbiZwJnqGfjaEJopWnBaqNrImvDbMJtAm3BbgNuIW6CbsJu4m+lb8FwAnGDcoNyxXMhc4J0YnTmdQF1YnXBdoF2wncBd2N3pXfEd+F44nmieiJ64Xtje8F8gnzCfON9IX2hfcF+YX6hfsJ/IYAhgQOCIYMig0SEIYUkhUOFYYbFhuGHg4hDicGKAYpBiueLQYzCjUKOQo6hj+GQoZFBkcGR4ZKhk6OT4pQBlGOUgZTClOGVIpWElcGW4pdDl4OYIpiBmKGYwpjjmUOZYZrDmuObY5uBnWKdo53BniSgQqBhoKWgwqDhoQShSqPBpIKkxKVBpaKmwadhp8Go4amCqaKpxaoBqoGqwavhrGGtYa3hroWvYrABsCGwwbFisaOyI7LjsyKzQbPhtIK1AbZGtoK2wrbhtyK3orghuQe5YbqiuuG7BbuBu6G8ArxhvKK9ZL2BvcK94r6BvsG/Qr+Bv6HCosMhxaLGYscCx+LIQchiyMHI4clkyaHJwsomykTK4stBy2TLgcwhzGHMhM1BzcHOJM8jz4HPw9DE0UHSotOB0+LUYtTC1QHWI9bB10HXYdhh2IHY49oD2iHaY9sh22Hbod3k3kHfAt/B4AHgIuDB4cLiAuIi4mPigeKl4uPjQ+Oj5ITkoeTB5QLlIeVD5cHl4eYh5oPmxuci54HoA+iB6aPpwenh6iPqo+rh64HsAuwh7ILtY+4B7mLuwe7j7wPvYu/B8OLxBvFB8mHywvSB9KH2IvZi90L3ZPgi+IT4ofkh+UL6Avoh+oH7AvtH++H8Bvwk/GH85f0i/UP+Qf9h", + "E4t/AAIAgQDBAQEBQQGBAaEB4wInAmEDogPBBCEHgQeiCAEIoQjCCSIJgwnhCsMLAQtjC6MMQQykDUENwg4hDmIOoQ7hDyEPYQ+iD+IQIhBiEKEQxBDhEQERYhHCEqETAhNDFIEUohUBFSIVRBWjF4EXoRgEGEIZQRlhGaIaIRrjG0Mc4x1hHgIeIx5CHwIfIR/EIMIhwyIiIoQjASNhJaMlxCaCJqEm4SciJ2MoRCiKKOEpBikhKaIqASrCKyErQSvBK+IsQSykLMEtQy2hLkEuYi7BLwEvQS+BL+MwYTEDMcEzoTPCNIE0oTTBNWE15DZhNqE2wjcBNyE3RTfCOCQ4QTihOOQ5ZDpBPCE8QTzBPWI+AT/hQIFAokEDQSJBgUJBQsZDQUPCQ+JFwUYkR2VIgkkESYNJo0uhTCFMYkzjTURNYk2CTuZPAVABUCFRJFFCUYJSAVJkUoFUAVQkVEFUgVTJVQFVIVYCVkNWwldhV6FYAViCWaJZ41oBWmNbolwEXIRdA16iXwJgIWCBYKFgwWFBYeJiRWKhY8Fk4mVCZcJl4mbhZ4FnoWfjaANogWijacFqo2tBbAJsImzCbQJtwW4BbwFvQm9lb6Vv5HACcIJxAXFhciJyxXLic4JzpHQFdEN0onUDdUF1YnZjdsJ3onjBeqF6wXrhe2N8JHxEfIJ9A30hfgF+Qn6jfwR/goABgSGBYoMBg8GD4YRhhOKFJIVChYOFwYXihsWG4YhCiGGJIYlBigGKgYtBi+KMwo1CjqGPAY8jkCGQopDCkcGSA5Jhk2GUAZTClOGVQZXBleGWBpbhl0OXhZjimQKZIplBmWGZ4ZoBmiGaQZpkmuObAZuDnCKdw53hnmKew58hn6SgQqBhoIKiAqKho8GkgaTBpUGlYqaGpuKnYafBqOGpoqnFqiGqgaqhqyKsAaylrYGuIq6BruGvobChsMGxAbGjscGzQbOjs+G1AbXBtgG2grcit4S3orghuMK5QblhukS64bsFu6G8ArxhvIK8orzkvQW9Ib1FvWS9gr3ivkG/Ir9CwKLCA8Mhw0HDYcOhxQTFosakxwPHgcflyKLJZMmDykfKY8qBy4LLwc1BzWLN4c4BziTOwc7kzwHPgdEh0WHSAdKB0qHTAtPk1MLU4tUB1STVQdWF10XYg9oD2iLag9rh22HbodwC3KLeQd8B38Hg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", + ]; + + let mut hll = HllInstance::new(2048).unwrap(); + + for d in data { + let s = base64::decode(d).unwrap(); + let h = HllInstance::read_hll_storage_spec(&s).unwrap(); + hll.merge_with(&h); + } + + assert_eq!(hll.cardinality(), 260925); + } } mod dense { From 30777c32877c4e70be73c1b8b49d18d0fb5e8990 Mon Sep 17 00:00:00 2001 From: Alexandr Romanenko Date: Tue, 26 Sep 2023 02:37:06 +0300 Subject: [PATCH 2/4] update --- rust/cubestore/Cargo.lock | 1 + rust/cubestore/cubehll/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/rust/cubestore/Cargo.lock b/rust/cubestore/Cargo.lock index 1847650406bc9..fbbbba3146b58 100644 --- a/rust/cubestore/Cargo.lock +++ b/rust/cubestore/Cargo.lock @@ -1189,6 +1189,7 @@ dependencies = [ name = "cubehll" version = "0.1.0" dependencies = [ + "base64 0.13.0", "byteorder", "hex", "itertools 0.10.1", diff --git a/rust/cubestore/cubehll/Cargo.toml b/rust/cubestore/cubehll/Cargo.toml index dc721b36e16f1..aca40efcfbf78 100644 --- a/rust/cubestore/cubehll/Cargo.toml +++ b/rust/cubestore/cubehll/Cargo.toml @@ -9,6 +9,7 @@ description = "HyperLogLog implementation ported from AirLift" [dev-dependencies] twox-hash = "1.6.0" hex = "0.4.2" +base64 = "0.13.0" [dependencies] byteorder = "1.4.2" From cc004f4939ba7344cce9eb7fcf416931e3affd4b Mon Sep 17 00:00:00 2001 From: Alexandr Romanenko Date: Tue, 26 Sep 2023 02:40:37 +0300 Subject: [PATCH 3/4] fmt --- rust/cubestore/cubehll/src/instance.rs | 32 ++++++++++---------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/rust/cubestore/cubehll/src/instance.rs b/rust/cubestore/cubehll/src/instance.rs index c0e6c09a5f891..015ecefa8e276 100644 --- a/rust/cubestore/cubehll/src/instance.rs +++ b/rust/cubestore/cubehll/src/instance.rs @@ -178,24 +178,24 @@ impl HllInstance { // TODO For now we read hll-storage-spec only for postgress. For other realizations // this algorithm can differ. /* - uint64_t mask = nregs - 1; + uint64_t mask = nregs - 1; - size_t maxregval = (1 << nbits) - 1; + size_t maxregval = (1 << nbits) - 1; - size_t ndx = elem & mask; + size_t ndx = elem & mask; - uint64_t ss_val = elem >> log2nregs; + uint64_t ss_val = elem >> log2nregs; - size_t p_w = ss_val == 0 ? 0 : __builtin_ctzll(ss_val) + 1; + size_t p_w = ss_val == 0 ? 0 : __builtin_ctzll(ss_val) + 1; - Assert(ndx < msc_regs_idx_limit()); + Assert(ndx < msc_regs_idx_limit()); - if (p_w > maxregval) - p_w = maxregval; + if (p_w > maxregval) + p_w = maxregval; - if (mscp->msc_regs[ndx] < p_w) - mscp->msc_regs[ndx] = p_w; - * */ + if (mscp->msc_regs[ndx] < p_w) + mscp->msc_regs[ndx] = p_w; + * */ let mask = num_buckets as u64 - 1; let mut indices = Vec::with_capacity(num_hashes); let mut values = Vec::with_capacity(num_hashes); @@ -211,16 +211,11 @@ impl HllInstance { val.trailing_zeros() + 1 }; - let zeroes = if zeroes > maxval { - maxval - } else { - zeroes - }; + let zeroes = if zeroes > maxval { maxval } else { zeroes }; indices.push(ind as u32); values.push(zeroes as u8); } - return Ok(HllInstance::Sparse(SparseHll::new_from_indices_and_values( log_num_buckets, indices, @@ -615,7 +610,6 @@ impl DenseHll { }; } - pub fn new_from_entries(index_bit_len: u8, values: Vec) -> Result { DenseHll::is_valid_bit_len(index_bit_len)?; let num_buckets = number_of_buckets(index_bit_len); @@ -1443,8 +1437,6 @@ mod tests { #[test] fn test_hll_postgr_test() { - - let t1 = "Eot/HUOjtjMdhpk="; let t2 = "FIt/OVKSkKYhSlQQhClKcpXEIRBEEKZJCHKlRjGKQhSlMwx0HQVRUFIdJkEOc5iEKY6TnIZJCFMRBUESgyimIhSDIKUxzGIgyCmWYpikIYxjHKVRknOVJzGMUyzoKUqTmKcpSIWQpTGMQ5CmQcxkEIgqiISoxzmWgpylQUxirQUhzGQkxDGQhJkjOhRyHKZBDlQRRjMSUhCmIVBTFKQqDGOcxUFOQpTlKkxSnMgqkJOU5iIKYxSGOY5TIQVRTGIMpiHOkozFKVRSlKgiSFIUpjlIchilQcpylGYZkIMghzHOgpzGQkpylIYhklMYpSFMYxTFKcxzHKkhyGOkpTmSQpjLcYpkFKhaCFMY6BnKYpknKdBUEOUxilMdBioMVCCIOYpzmSkxkGIgxSmQYpyqOcxjIKUpEHMc5VGMopzmKgxjJKYpiEGghjGKZRUFQQyTEMc5zGQcx0GGYhCqKQ6UnKc5inOUhUFIcpzlMUyEHOQxkJWUxkJQlCTlYRCymKchVJOdBzFQY5TlMUhiHMVJSlKUpkGIopSnUUhjFQcpjKQQpjoMYxDKKkxDpSYqCHUZJToQcijrIchTjI0x0GMciCKUghUJKkxTIQhJjGOYylHMwxzlIlSCnOkqCJKcpllOQiVHKZAzpOYhkHMU6zFGYpylKhRznKQxCGIQ5DFMYxmkKQxTFKYxznQUxCJMZRiLKUpzHQUyCoKVCTKKYpSkKg6UHIlBSmQYxTlQYqDIIdJimKY5zIKYpiGMcxylOkqCFQVJylOcxSmKkpjIIZRWEKUpTGIY5xsMdB0HKk6BjGYxjFKQxjmQZBSlMYhEpMlRSHKU5TmKQhjIKYxyEMU6DmMcpkFOgxEEKQpToScxxkMVBCIKVCVGOY5UqQYxikKUxCKSY5CnWQ6TlIg6TFKURTGKZCUJSlRRqKZJ2EOgiSESMpDmQQpyISRCDmOopilIQpSIeUo0lMYiSGKcpynQUhDkKRBDoOVpTIIU5SGKUhjIKcxTHIYxGEQY5UkIUhjHQUxDoGUiTlOUhzmGg6imOY5ilKUpCFQZBjGKYhjEMUhRqMUyCnMQxTIQkyTGMkwzHQg5RmKQiTJKQ50FORpypMVBxlKRBTLMcpEGMZBjlMg5TpMUqkpQkxjHScqjlUgqSmOVJlHIYxhmIQyEGWkxyEOMpTEKY5SoQZhjHQUpzGWVChlKghymOZhDmQYpEnQcpDnOUpTGGY5jlMc5TkKUxzFOcxSmIkpToQU5DoMgyhmIhRimIYhykKU5jmQk6FHcUpjJIc5TFIU5UoIUwzGKUhjJScpilMhJDFKNBjHKkpUHMYhkHQNBUEQpSDHOghhkKYpRmKYpzGOYxjFIYpjleY53FGU5SmKYpiJMRDjlMpBiHKgyhoMU5iHUUqEKOg5CJOUg0GOcpiESghDkac6UESUyTHSUy0FOgqDGUU5TGMUyTHKUpkGMdBzpGlBCFMU5TmMY5UGQYyEmKQxCnMRZyGKYqDJKZBilOUx0HIZBTFKMxiJSg5TEYQxUJOkxDlOYZ0EKRCTHOUxknQQh1FKQpSIOdBUnMVQzHKQ5imWsxhlQpBSoKZCDMIk5zQMUpilOMxUGIUxiFMU6THOUplEIYxyFORR1mKZJTFKUoyrIYhTFKY6DmOkiEFKZJEKMYhitIgyyGKUxjkKgxTmMZBCjKQpDmScpjkQchyFQYhinQhIzKMQ="; let t3 = "FIt/OVKSkKYhSlQQhClKcpXEIRBEEKZJCHKlRjGKQhSlMwx0HQVRUFIdJkEOc5iEKY6TnIZJCFMRBUESgyimIhSDIKUxzGIgyCmWYpikIYxjHKVRknOVJzGMUyzoKUqTmKcpSIWQpTGMQ5CmQcxkEIgqiISoxzmWgpylQUxirQUhzGQkxDGQhJkjOhRyHKZBDlQRRjMSUhCmIVBTFKQqDGOcxUFOQpTlKkxSnMgqkJOU5iIKYxSGOY5TIQVRTGIMpiHOkozFKVRSlKgiSFIUpjlIchilQcpylGYZkIMghzHOgpzGQkpylIYhklMYpSFMYxTFKcxzHKkhyGOkpTmSQpjLcYpkFKhaCFMY6BnKYpknKdBUEOUxilMdBioMVCCIOYpzmSkxkGIgxSmQYpyqOcxjIKUpEHMc5VGMopzmKgxjJKYpiEGghjGKZRUFQQyTEMc5zGQcx0GGYhCqKQ6UnKc5inOUhUFIcpzlMUyEHOQxkJWUxkJQlCTlYRCymKchVJOdBzFQY5TlMUhiHMVJSlKUpkGIopSnUUhjFQcpjKQQpjoMYxDKKkxDpSYqCHUZJToQcijrIchTjI0x0GMciCKUghUJKkxTIQhJjGOYylHMwxzlIlSCnOkqCJKcpllOQiVHKZAzpOYhkHMU6zFGYpylKhRznKQxCGIQ5DFMYxmkKQxTFKYxznQUxCJMZRiLKUpzHQUyCoKVCTKKYpSkKg6UHIlBSmQYxTlQYqDIIdJimKY5zIKYpiGMcxylOkqCFQVJylOcxSmKkpjIIZRWEKUpTGIY5xsMdB0HKk6BjGYxjFKQxjmQZBSlMYhEpMlRSHKU5TmKQhjIKYxyEMU6DmMcpkFOgxEEKQpToScxxkMVBCIKVCVGOY5UqQYxikKUxCKSY5CnWQ6TlIg6TFKURTGKZCUJSlRRqKZJ2EOgiSESMpDmQQpyISRCDmOopilIQpSIeUo0lMYiSGKcpynQUhDkKRBDoOVpTIIU5SGKUhjIKcxTHIYxGEQY5UkIUhjHQUxDoGUiTlOUhzmGg6imOY5ilKUpCFQZBjGKYhjEMUhRqMUyCnMQxTIQkyTGMkwzHQg5RmKQiTJKQ50FORpypMVBxlKRBTLMcpEGMZBjlMg5TpMUqkpQkxjHScqjlUgqSmOVJlHIYxhmIQyEGWkxyEOMpTEKY5SoQZhjHQUpzGWVChlKghymOZhDmQYpEnQcpDnOUpTGGY5jlMc5TkKUxzFOcxSmIkpToQU5DoMgyhmIhRimIYhykKU5jmQk6FHcUpjJIc5TFIU5UoIUwzGKUhjJScpilMhJDFKNBjHKkpUHMYhkHQNBUEQpSDHOghhkKYpRmKYpzGOYxjFIYpjleY53FGU5SmKYpiJMRDjlMpBiHKgyhoMU5iHUUqEKOg5CJOUhUGOcpiESghDkac6UESUyTHSUy0FOgqDGUU5TGMUyTHKUpkGMdBzpGlBCFMU5TmMY5UGQYyEmKQxCnMRZyGKYqDJKZBilOUx0HIZBTFKMxiJSg5TEYQxUJOkxDlOYZ0EKRCTHOUxknQQh1FKQpSIOdBUnMVQzHKQ5imWsxhlQpBSoKZCDMIk5zQMUpilOMxUGIUxiFMU6THOUplEIYxyFORR1mKZJTFKUoyrIYhTFKY6DmOkiEFKZJEKMYhitIgyyGKUxjkKgxTmMZBCjKQpDmScpjkQchyFQYhinQhIzKMQ="; From d108822a576551c67df72e566468ee8b7849a65c Mon Sep 17 00:00:00 2001 From: Alexandr Romanenko Date: Tue, 26 Sep 2023 03:52:23 +0300 Subject: [PATCH 4/4] update --- rust/cubestore/cubehll/src/instance.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/cubestore/cubehll/src/instance.rs b/rust/cubestore/cubehll/src/instance.rs index 015ecefa8e276..a08849cff3aea 100644 --- a/rust/cubestore/cubehll/src/instance.rs +++ b/rust/cubestore/cubehll/src/instance.rs @@ -175,8 +175,8 @@ impl HllInstance { ))); } // Convert to sparse representation in Airlift. By analogy with the code from postgress hll: - // TODO For now we read hll-storage-spec only for postgress. For other realizations - // this algorithm can differ. + // TODO Algorithm of conversion from EXPLICIT to SPARSE is not part of the storage specification, + // so we use the algorithm from the PostgreSql as is. /* uint64_t mask = nregs - 1;