/// <summary> /// Merges the given CardinalityEstimator instances and returns the result /// </summary> /// <param name="estimators">Instances of CardinalityEstimator</param> /// <returns>The merged CardinalityEstimator</returns> public static CardinalityEstimator Merge(IList <CardinalityEstimator> estimators) { if (!estimators.Any()) { throw new ArgumentException(string.Format("Was asked to merge 0 instances of {0}", typeof(CardinalityEstimator)), "estimators"); } var ans = new CardinalityEstimator(estimators[0].bitsPerIndex); foreach (CardinalityEstimator estimator in estimators) { ans.Merge(estimator); } return(ans); }
/// <summary> /// Merges the given CardinalityEstimator instances and returns the result /// </summary> /// <param name="estimators">Instances of CardinalityEstimator</param> /// <returns>The merged CardinalityEstimator</returns> public static CardinalityEstimator Merge(IList <CardinalityEstimator> estimators) { if (!estimators.Any()) { throw new ArgumentException($"Was asked to merge 0 instances of {typeof(CardinalityEstimator)}", nameof(estimators)); } var ans = new CardinalityEstimator(estimators[0]._bitsPerIndex); foreach (CardinalityEstimator estimator in estimators) { ans.Merge(estimator); } return(ans); }
/// <summary> /// Serialize the given <paramref name="cardinalityEstimator" /> to <paramref name="stream" /> /// </summary> public void Serialize(Stream stream, CardinalityEstimator cardinalityEstimator) { using (var bw = new BinaryWriter(stream)) { bw.Write(DataFormatMajorVersion); bw.Write(DataFormatMinorVersion); CardinalityEstimatorState data = cardinalityEstimator.GetState(); bw.Write((byte)data.HashFunctionId); bw.Write(data.BitsPerIndex); bw.Write((byte)(((data.IsSparse ? 1 : 0) << 1) + (data.DirectCount != null ? 1 : 0))); if (data.DirectCount != null) { bw.Write(data.DirectCount.Count); foreach (ulong element in data.DirectCount) { bw.Write(element); } } else if (data.IsSparse) { bw.Write(data.LookupSparse.Count); foreach (KeyValuePair <ushort, byte> element in data.LookupSparse) { bw.Write(element.Key); bw.Write(element.Value); } } else { bw.Write(data.LookupDense.Length); foreach (byte element in data.LookupDense) { bw.Write(element); } } bw.Write(data.CountAdditions); bw.Flush(); } }
/// <summary> /// Deserialize a <see cref="CardinalityEstimator" /> from the given <paramref name="stream" /> /// </summary> public CardinalityEstimator Deserialize(Stream stream) { using (var br = new BinaryReader(stream)) { int dataFormatMajorVersion = br.ReadUInt16(); int dataFormatMinorVersion = br.ReadUInt16(); AssertDataVersionCanBeRead(dataFormatMajorVersion, dataFormatMinorVersion); HashFunctionId hashFunctionId; if (dataFormatMajorVersion >= 2) { // Starting with version 2.0, the serializer writes the hash function ID hashFunctionId = (HashFunctionId)br.ReadByte(); } else { // Versions before 2.0 all used FNV-1a hashFunctionId = HashFunctionId.Fnv1A; } int bitsPerIndex = br.ReadInt32(); byte flags = br.ReadByte(); bool isSparse = ((flags & 2) == 2); bool isDirectCount = ((flags & 1) == 1); HashSet <ulong> directCount = null; IDictionary <ushort, byte> lookupSparse = isSparse ? new Dictionary <ushort, byte>() : null; byte[] lookupDense = null; if (isDirectCount) { int count = br.ReadInt32(); directCount = new HashSet <ulong>(); for (var i = 0; i < count; i++) { ulong element = br.ReadUInt64(); directCount.Add(element); } } else if (isSparse) { int count = br.ReadInt32(); for (var i = 0; i < count; i++) { ushort elementKey = br.ReadUInt16(); byte elementValue = br.ReadByte(); lookupSparse.Add(elementKey, elementValue); } } else { int count = br.ReadInt32(); lookupDense = br.ReadBytes(count); } // Starting with version 2.1, the serializer writes CountAdditions ulong countAdditions = 0UL; if (dataFormatMajorVersion >= 2 && dataFormatMinorVersion >= 1) { countAdditions = br.ReadUInt64(); } var data = new CardinalityEstimatorState { HashFunctionId = hashFunctionId, BitsPerIndex = bitsPerIndex, DirectCount = directCount, IsSparse = isSparse, LookupDense = lookupDense, LookupSparse = lookupSparse, CountAdditions = countAdditions, }; var result = new CardinalityEstimator(data); return(result); } }
/// <summary> /// Merges the given <paramref name="other" /> CardinalityEstimator instance into this one /// </summary> /// <param name="other">another instance of CardinalityEstimator</param> public void Merge(CardinalityEstimator other) { if (other == null) { throw new ArgumentNullException("other"); } if (other.m != this.m) { throw new ArgumentOutOfRangeException("other", "Cannot merge CardinalityEstimator instances with different accuracy/map sizes"); } this.CountAdditions += other.CountAdditions; if (this.isSparse && other.isSparse) { // Merge two sparse instances foreach (KeyValuePair <ushort, byte> kvp in other.lookupSparse) { ushort index = kvp.Key; byte otherRank = kvp.Value; byte thisRank; this.lookupSparse.TryGetValue(index, out thisRank); this.lookupSparse[index] = Math.Max(thisRank, otherRank); } // Switch to dense if necessary if (this.lookupSparse.Count > this.sparseMaxElements) { SwitchToDenseRepresentation(); } } else { // Make sure this (target) instance is dense, then merge SwitchToDenseRepresentation(); if (other.isSparse) { foreach (KeyValuePair <ushort, byte> kvp in other.lookupSparse) { ushort index = kvp.Key; byte rank = kvp.Value; this.lookupDense[index] = Math.Max(this.lookupDense[index], rank); } } else { for (var i = 0; i < this.m; i++) { this.lookupDense[i] = Math.Max(this.lookupDense[i], other.lookupDense[i]); } } } if (other.directCount != null) { // Other instance is using direct counter. If this instance is also using direct counter, merge them. if (this.directCount != null) { this.directCount.UnionWith(other.directCount); } } else { // Other instance is not using direct counter, make sure this instance doesn't either this.directCount = null; } }