public static void EncodePgoData <TType>(IEnumerable <PgoSchemaElem> schemas, IPgoEncodedValueEmitter <TType> valueEmitter, bool emitAllElementsUnconditionally) { PgoSchemaElem prevSchema = default(PgoSchemaElem); TType prevEmittedType = default(TType); long prevEmittedIntData = 0; foreach (PgoSchemaElem schema in schemas) { int ilOffsetDiff = schema.ILOffset - prevSchema.ILOffset; int OtherDiff = schema.Other - prevSchema.Other; int CountDiff = schema.Count - prevSchema.Count; int TypeDiff = (int)schema.InstrumentationKind - (int)prevSchema.InstrumentationKind; InstrumentationDataProcessingState modifyMask = (InstrumentationDataProcessingState)0; if (!emitAllElementsUnconditionally) { if (ilOffsetDiff != 0) { modifyMask = modifyMask | InstrumentationDataProcessingState.ILOffset; } if (TypeDiff != 0) { modifyMask = modifyMask | InstrumentationDataProcessingState.Type; } if (CountDiff != 0) { modifyMask = modifyMask | InstrumentationDataProcessingState.Count; } if (OtherDiff != 0) { modifyMask = modifyMask | InstrumentationDataProcessingState.Other; } } else { modifyMask = InstrumentationDataProcessingState.ILOffset | InstrumentationDataProcessingState.Type | InstrumentationDataProcessingState.Count | InstrumentationDataProcessingState.Other; } Debug.Assert(modifyMask != InstrumentationDataProcessingState.Done); valueEmitter.EmitLong((long)modifyMask, 0); if ((modifyMask & InstrumentationDataProcessingState.ILOffset) == InstrumentationDataProcessingState.ILOffset) { valueEmitter.EmitLong(schema.ILOffset, prevSchema.ILOffset); } if ((modifyMask & InstrumentationDataProcessingState.Type) == InstrumentationDataProcessingState.Type) { valueEmitter.EmitLong((long)schema.InstrumentationKind, (long)prevSchema.InstrumentationKind); } if ((modifyMask & InstrumentationDataProcessingState.Count) == InstrumentationDataProcessingState.Count) { valueEmitter.EmitLong(schema.Count, prevSchema.Count); } if ((modifyMask & InstrumentationDataProcessingState.Other) == InstrumentationDataProcessingState.Other) { valueEmitter.EmitLong(schema.Other, prevSchema.Other); } for (int i = 0; i < schema.Count; i++) { switch (schema.InstrumentationKind & PgoInstrumentationKind.MarshalMask) { case PgoInstrumentationKind.None: break; case PgoInstrumentationKind.FourByte: { long valueToEmit; if (schema.Count == 1) { valueToEmit = schema.DataLong; } else { valueToEmit = ((int[])schema.DataObject)[i]; } valueEmitter.EmitLong(valueToEmit, prevEmittedIntData); prevEmittedIntData = valueToEmit; break; } case PgoInstrumentationKind.EightByte: { long valueToEmit; if (schema.Count == 1) { valueToEmit = schema.DataLong; } else { valueToEmit = ((long[])schema.DataObject)[i]; } valueEmitter.EmitLong(valueToEmit, prevEmittedIntData); prevEmittedIntData = valueToEmit; break; } case PgoInstrumentationKind.TypeHandle: { TType typeToEmit = ((TType[])schema.DataObject)[i]; valueEmitter.EmitType(typeToEmit, prevEmittedType); prevEmittedType = typeToEmit; break; } } } prevSchema = schema; } // Emit a "done" schema if (!valueEmitter.EmitDone()) { // If EmitDone returns true, no further data needs to be encoded. // Otherwise, emit a "Done" schema valueEmitter.EmitLong((long)InstrumentationDataProcessingState.Type, 0); valueEmitter.EmitLong((long)PgoInstrumentationKind.Done, (long)prevSchema.InstrumentationKind); } }
public static PgoSchemaElem[] Merge <TType>(ReadOnlySpan <PgoSchemaElem[]> schemasToMerge) { { // The merging algorithm will sort the schema data by iloffset, then by InstrumentationKind // From there each individual instrumentation kind shall have a specific merging rule Dictionary <PgoSchemaElem, PgoSchemaElem> dataMerger = new Dictionary <PgoSchemaElem, PgoSchemaElem>(PgoSchemaMergeComparer.Singleton); foreach (PgoSchemaElem[] schemaSet in schemasToMerge) { bool foundNumRuns = false; foreach (PgoSchemaElem schema in schemaSet) { if (schema.InstrumentationKind == PgoInstrumentationKind.NumRuns) { foundNumRuns = true; } MergeInSchemaElem(dataMerger, schema); } if (!foundNumRuns) { PgoSchemaElem oneRunSchema = new PgoSchemaElem(); oneRunSchema.InstrumentationKind = PgoInstrumentationKind.NumRuns; oneRunSchema.ILOffset = 0; oneRunSchema.Other = 1; oneRunSchema.Count = 1; MergeInSchemaElem(dataMerger, oneRunSchema); } } PgoSchemaElem[] result = new PgoSchemaElem[dataMerger.Count]; dataMerger.Values.CopyTo(result, 0); Array.Sort(result, PgoSchemaMergeComparer.Singleton); return(result); } void MergeInSchemaElem(Dictionary <PgoSchemaElem, PgoSchemaElem> dataMerger, PgoSchemaElem schema) { long sortKey = ((long)schema.ILOffset) << 32 | (long)schema.InstrumentationKind; if (dataMerger.TryGetValue(schema, out var existingSchemaItem)) { // Actually merge two schema items PgoSchemaElem mergedElem = existingSchemaItem; switch (existingSchemaItem.InstrumentationKind) { case PgoInstrumentationKind.BasicBlockIntCount: case PgoInstrumentationKind.TypeHandleHistogramCount: if ((existingSchemaItem.Count != 1) || (schema.Count != 1)) { throw new Exception("Unable to merge pgo data. Invalid format"); } mergedElem.DataLong = existingSchemaItem.DataLong + schema.DataLong; break; case PgoInstrumentationKind.TypeHandleHistogramTypeHandle: { mergedElem.Count = existingSchemaItem.Count + schema.Count; TType[] newMergedTypeArray = new TType[mergedElem.Count]; mergedElem.DataObject = newMergedTypeArray; int i = 0; foreach (TType type in (TType[])existingSchemaItem.DataObject) { newMergedTypeArray[i++] = type; } foreach (TType type in (TType[])schema.DataObject) { newMergedTypeArray[i++] = type; } break; } case PgoInstrumentationKind.Version: { mergedElem.Other = Math.Max(existingSchemaItem.Other, schema.Other); break; } case PgoInstrumentationKind.NumRuns: { mergedElem.Other = existingSchemaItem.Other + schema.Other; break; } } Debug.Assert(PgoSchemaMergeComparer.Singleton.Compare(schema, mergedElem) == 0); Debug.Assert(PgoSchemaMergeComparer.Singleton.Equals(schema, mergedElem) == true); dataMerger[mergedElem] = mergedElem; } else { dataMerger.Add(schema, schema); } } }
public static IEnumerable <PgoSchemaElem> ParsePgoData <TType>(IPgoSchemaDataLoader <TType> dataProvider, IEnumerable <long> inputDataStream, bool longsAreCompressed) { int dataCountToRead = 0; PgoSchemaElem curSchema = default(PgoSchemaElem); InstrumentationDataProcessingState processingState = InstrumentationDataProcessingState.UpdateProcessMaskFlag; long lastDataValue = 0; long lastTypeValue = 0; foreach (long value in inputDataStream) { if (dataCountToRead > 0) { if (curSchema.DataHeldInDataLong) { if (longsAreCompressed) { lastDataValue += value; } else { lastDataValue = value; } curSchema.DataLong = lastDataValue; } else { int dataIndex = curSchema.Count - dataCountToRead; switch (curSchema.InstrumentationKind & PgoInstrumentationKind.MarshalMask) { case PgoInstrumentationKind.FourByte: if (longsAreCompressed) { lastDataValue += value; } else { lastDataValue = value; } ((int[])curSchema.DataObject)[dataIndex] = checked ((int)lastDataValue); break; case PgoInstrumentationKind.EightByte: if (longsAreCompressed) { lastDataValue += value; } else { lastDataValue = value; } ((long[])curSchema.DataObject)[dataIndex] = lastDataValue; break; case PgoInstrumentationKind.TypeHandle: if (longsAreCompressed) { lastTypeValue += value; } else { lastTypeValue = value; } ((TType[])curSchema.DataObject)[dataIndex] = dataProvider.TypeFromLong(lastTypeValue); break; } } dataCountToRead--; if (dataCountToRead == 0) { yield return(curSchema); curSchema.DataLong = 0; curSchema.DataObject = null; } continue; } if (processingState == InstrumentationDataProcessingState.UpdateProcessMaskFlag) { processingState = (InstrumentationDataProcessingState)value; continue; } if ((processingState & InstrumentationDataProcessingState.ILOffset) == InstrumentationDataProcessingState.ILOffset) { if (longsAreCompressed) { curSchema.ILOffset = checked ((int)(value + (long)curSchema.ILOffset)); } else { curSchema.ILOffset = checked ((int)value); } processingState = processingState & ~InstrumentationDataProcessingState.ILOffset; } else if ((processingState & InstrumentationDataProcessingState.Type) == InstrumentationDataProcessingState.Type) { if (longsAreCompressed) { curSchema.InstrumentationKind = (PgoInstrumentationKind)(((int)(curSchema.InstrumentationKind)) + checked ((int)value)); } else { curSchema.InstrumentationKind = (PgoInstrumentationKind)value; } processingState = processingState & ~InstrumentationDataProcessingState.Type; } else if ((processingState & InstrumentationDataProcessingState.Count) == InstrumentationDataProcessingState.Count) { if (longsAreCompressed) { curSchema.Count = checked ((int)(value + (long)curSchema.Count)); } else { curSchema.Count = checked ((int)value); } processingState = processingState & ~InstrumentationDataProcessingState.Count; } else if ((processingState & InstrumentationDataProcessingState.Other) == InstrumentationDataProcessingState.Other) { if (longsAreCompressed) { curSchema.Other = checked ((int)(value + (long)curSchema.Other)); } else { curSchema.Other = checked ((int)value); } processingState = processingState & ~InstrumentationDataProcessingState.Other; } if (processingState == InstrumentationDataProcessingState.Done) { processingState = InstrumentationDataProcessingState.UpdateProcessMaskFlag; if (curSchema.InstrumentationKind == PgoInstrumentationKind.Done) { yield break; } switch (curSchema.InstrumentationKind & PgoInstrumentationKind.MarshalMask) { case PgoInstrumentationKind.None: yield return(curSchema); break; case PgoInstrumentationKind.FourByte: if (curSchema.Count > 1) { curSchema.DataObject = new int[curSchema.Count]; } dataCountToRead = curSchema.Count; break; case PgoInstrumentationKind.EightByte: if (curSchema.Count > 1) { curSchema.DataObject = new long[curSchema.Count]; } dataCountToRead = curSchema.Count; break; case PgoInstrumentationKind.TypeHandle: curSchema.DataObject = new TType[curSchema.Count]; dataCountToRead = curSchema.Count; break; default: throw new Exception("Unknown Type"); } } } throw new Exception("Partial Instrumentation Data"); }