// Copyright (c) 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "stats_analyzer.h" #include #include #include #include #include #include #include #include #include "spirv/1.2/spirv.h" #include "source/enum_string_mapping.h" #include "source/comp/markv_model.h" #include "source/opcode.h" #include "source/operand.h" #include "source/spirv_constant.h" #include "source/util/huffman_codec.h" using libspirv::SpirvStats; using spvutils::HuffmanCodec; namespace { // Signals that the value is not in the coding scheme and a fallback method // needs to be used. const uint64_t kMarkvNoneOfTheAbove = spvtools::MarkvModel::GetMarkvNoneOfTheAbove(); inline uint32_t CombineOpcodeAndNumOperands(uint32_t opcode, uint32_t num_operands) { return opcode | (num_operands << 16); } // Returns all SPIR-V v1.2 opcodes. std::vector GetAllOpcodes() { return std::vector({ SpvOpNop, SpvOpUndef, SpvOpSourceContinued, SpvOpSource, SpvOpSourceExtension, SpvOpName, SpvOpMemberName, SpvOpString, SpvOpLine, SpvOpExtension, SpvOpExtInstImport, SpvOpExtInst, SpvOpMemoryModel, SpvOpEntryPoint, SpvOpExecutionMode, SpvOpCapability, SpvOpTypeVoid, SpvOpTypeBool, SpvOpTypeInt, SpvOpTypeFloat, SpvOpTypeVector, SpvOpTypeMatrix, SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage, SpvOpTypeArray, SpvOpTypeRuntimeArray, SpvOpTypeStruct, SpvOpTypeOpaque, SpvOpTypePointer, SpvOpTypeFunction, SpvOpTypeEvent, SpvOpTypeDeviceEvent, SpvOpTypeReserveId, SpvOpTypeQueue, SpvOpTypePipe, SpvOpTypeForwardPointer, SpvOpConstantTrue, SpvOpConstantFalse, SpvOpConstant, SpvOpConstantComposite, SpvOpConstantSampler, SpvOpConstantNull, SpvOpSpecConstantTrue, SpvOpSpecConstantFalse, SpvOpSpecConstant, SpvOpSpecConstantComposite, SpvOpSpecConstantOp, SpvOpFunction, SpvOpFunctionParameter, SpvOpFunctionEnd, SpvOpFunctionCall, SpvOpVariable, SpvOpImageTexelPointer, SpvOpLoad, SpvOpStore, SpvOpCopyMemory, SpvOpCopyMemorySized, SpvOpAccessChain, SpvOpInBoundsAccessChain, SpvOpPtrAccessChain, SpvOpArrayLength, SpvOpGenericPtrMemSemantics, SpvOpInBoundsPtrAccessChain, SpvOpDecorate, SpvOpMemberDecorate, SpvOpDecorationGroup, SpvOpGroupDecorate, SpvOpGroupMemberDecorate, SpvOpVectorExtractDynamic, SpvOpVectorInsertDynamic, SpvOpVectorShuffle, SpvOpCompositeConstruct, SpvOpCompositeExtract, SpvOpCompositeInsert, SpvOpCopyObject, SpvOpTranspose, SpvOpSampledImage, SpvOpImageSampleImplicitLod, SpvOpImageSampleExplicitLod, SpvOpImageSampleDrefImplicitLod, SpvOpImageSampleDrefExplicitLod, SpvOpImageSampleProjImplicitLod, SpvOpImageSampleProjExplicitLod, SpvOpImageSampleProjDrefImplicitLod, SpvOpImageSampleProjDrefExplicitLod, SpvOpImageFetch, SpvOpImageGather, SpvOpImageDrefGather, SpvOpImageRead, SpvOpImageWrite, SpvOpImage, SpvOpImageQueryFormat, SpvOpImageQueryOrder, SpvOpImageQuerySizeLod, SpvOpImageQuerySize, SpvOpImageQueryLod, SpvOpImageQueryLevels, SpvOpImageQuerySamples, SpvOpConvertFToU, SpvOpConvertFToS, SpvOpConvertSToF, SpvOpConvertUToF, SpvOpUConvert, SpvOpSConvert, SpvOpFConvert, SpvOpQuantizeToF16, SpvOpConvertPtrToU, SpvOpSatConvertSToU, SpvOpSatConvertUToS, SpvOpConvertUToPtr, SpvOpPtrCastToGeneric, SpvOpGenericCastToPtr, SpvOpGenericCastToPtrExplicit, SpvOpBitcast, SpvOpSNegate, SpvOpFNegate, SpvOpIAdd, SpvOpFAdd, SpvOpISub, SpvOpFSub, SpvOpIMul, SpvOpFMul, SpvOpUDiv, SpvOpSDiv, SpvOpFDiv, SpvOpUMod, SpvOpSRem, SpvOpSMod, SpvOpFRem, SpvOpFMod, SpvOpVectorTimesScalar, SpvOpMatrixTimesScalar, SpvOpVectorTimesMatrix, SpvOpMatrixTimesVector, SpvOpMatrixTimesMatrix, SpvOpOuterProduct, SpvOpDot, SpvOpIAddCarry, SpvOpISubBorrow, SpvOpUMulExtended, SpvOpSMulExtended, SpvOpAny, SpvOpAll, SpvOpIsNan, SpvOpIsInf, SpvOpIsFinite, SpvOpIsNormal, SpvOpSignBitSet, SpvOpLessOrGreater, SpvOpOrdered, SpvOpUnordered, SpvOpLogicalEqual, SpvOpLogicalNotEqual, SpvOpLogicalOr, SpvOpLogicalAnd, SpvOpLogicalNot, SpvOpSelect, SpvOpIEqual, SpvOpINotEqual, SpvOpUGreaterThan, SpvOpSGreaterThan, SpvOpUGreaterThanEqual, SpvOpSGreaterThanEqual, SpvOpULessThan, SpvOpSLessThan, SpvOpULessThanEqual, SpvOpSLessThanEqual, SpvOpFOrdEqual, SpvOpFUnordEqual, SpvOpFOrdNotEqual, SpvOpFUnordNotEqual, SpvOpFOrdLessThan, SpvOpFUnordLessThan, SpvOpFOrdGreaterThan, SpvOpFUnordGreaterThan, SpvOpFOrdLessThanEqual, SpvOpFUnordLessThanEqual, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThanEqual, SpvOpShiftRightLogical, SpvOpShiftRightArithmetic, SpvOpShiftLeftLogical, SpvOpBitwiseOr, SpvOpBitwiseXor, SpvOpBitwiseAnd, SpvOpNot, SpvOpBitFieldInsert, SpvOpBitFieldSExtract, SpvOpBitFieldUExtract, SpvOpBitReverse, SpvOpBitCount, SpvOpDPdx, SpvOpDPdy, SpvOpFwidth, SpvOpDPdxFine, SpvOpDPdyFine, SpvOpFwidthFine, SpvOpDPdxCoarse, SpvOpDPdyCoarse, SpvOpFwidthCoarse, SpvOpEmitVertex, SpvOpEndPrimitive, SpvOpEmitStreamVertex, SpvOpEndStreamPrimitive, SpvOpControlBarrier, SpvOpMemoryBarrier, SpvOpAtomicLoad, SpvOpAtomicStore, SpvOpAtomicExchange, SpvOpAtomicCompareExchange, SpvOpAtomicCompareExchangeWeak, SpvOpAtomicIIncrement, SpvOpAtomicIDecrement, SpvOpAtomicIAdd, SpvOpAtomicISub, SpvOpAtomicSMin, SpvOpAtomicUMin, SpvOpAtomicSMax, SpvOpAtomicUMax, SpvOpAtomicAnd, SpvOpAtomicOr, SpvOpAtomicXor, SpvOpPhi, SpvOpLoopMerge, SpvOpSelectionMerge, SpvOpLabel, SpvOpBranch, SpvOpBranchConditional, SpvOpSwitch, SpvOpKill, SpvOpReturn, SpvOpReturnValue, SpvOpUnreachable, SpvOpLifetimeStart, SpvOpLifetimeStop, SpvOpGroupAsyncCopy, SpvOpGroupWaitEvents, SpvOpGroupAll, SpvOpGroupAny, SpvOpGroupBroadcast, SpvOpGroupIAdd, SpvOpGroupFAdd, SpvOpGroupFMin, SpvOpGroupUMin, SpvOpGroupSMin, SpvOpGroupFMax, SpvOpGroupUMax, SpvOpGroupSMax, SpvOpReadPipe, SpvOpWritePipe, SpvOpReservedReadPipe, SpvOpReservedWritePipe, SpvOpReserveReadPipePackets, SpvOpReserveWritePipePackets, SpvOpCommitReadPipe, SpvOpCommitWritePipe, SpvOpIsValidReserveId, SpvOpGetNumPipePackets, SpvOpGetMaxPipePackets, SpvOpGroupReserveReadPipePackets, SpvOpGroupReserveWritePipePackets, SpvOpGroupCommitReadPipe, SpvOpGroupCommitWritePipe, SpvOpEnqueueMarker, SpvOpEnqueueKernel, SpvOpGetKernelNDrangeSubGroupCount, SpvOpGetKernelNDrangeMaxSubGroupSize, SpvOpGetKernelWorkGroupSize, SpvOpGetKernelPreferredWorkGroupSizeMultiple, SpvOpRetainEvent, SpvOpReleaseEvent, SpvOpCreateUserEvent, SpvOpIsValidEvent, SpvOpSetUserEventStatus, SpvOpCaptureEventProfilingInfo, SpvOpGetDefaultQueue, SpvOpBuildNDRange, SpvOpImageSparseSampleImplicitLod, SpvOpImageSparseSampleExplicitLod, SpvOpImageSparseSampleDrefImplicitLod, SpvOpImageSparseSampleDrefExplicitLod, SpvOpImageSparseSampleProjImplicitLod, SpvOpImageSparseSampleProjExplicitLod, SpvOpImageSparseSampleProjDrefImplicitLod, SpvOpImageSparseSampleProjDrefExplicitLod, SpvOpImageSparseFetch, SpvOpImageSparseGather, SpvOpImageSparseDrefGather, SpvOpImageSparseTexelsResident, SpvOpNoLine, SpvOpAtomicFlagTestAndSet, SpvOpAtomicFlagClear, SpvOpImageSparseRead, SpvOpSizeOf, SpvOpTypePipeStorage, SpvOpConstantPipeStorage, SpvOpCreatePipeFromPipeStorage, SpvOpGetKernelLocalSizeForSubgroupCount, SpvOpGetKernelMaxNumSubgroups, SpvOpTypeNamedBarrier, SpvOpNamedBarrierInitialize, SpvOpMemoryNamedBarrier, SpvOpModuleProcessed, SpvOpExecutionModeId, SpvOpDecorateId, SpvOpSubgroupBallotKHR, SpvOpSubgroupFirstInvocationKHR, SpvOpSubgroupAllKHR, SpvOpSubgroupAnyKHR, SpvOpSubgroupAllEqualKHR, SpvOpSubgroupReadInvocationKHR, }); } std::string GetVersionString(uint32_t word) { std::stringstream ss; ss << "Version " << SPV_SPIRV_VERSION_MAJOR_PART(word) << "." << SPV_SPIRV_VERSION_MINOR_PART(word); return ss.str(); } std::string GetGeneratorString(uint32_t word) { return spvGeneratorStr(SPV_GENERATOR_TOOL_PART(word)); } std::string GetOpcodeString(uint32_t word) { return spvOpcodeString(static_cast(word)); } std::string GetCapabilityString(uint32_t word) { return libspirv::CapabilityToString(static_cast(word)); } template std::string KeyIsLabel(T key) { std::stringstream ss; ss << key; return ss.str(); } template std::unordered_map GetRecall( const std::unordered_map& hist, uint64_t total) { std::unordered_map freq; for (const auto& pair : hist) { const double frequency = static_cast(pair.second) / static_cast(total); freq.emplace(pair.first, frequency); } return freq; } template std::unordered_map GetPrevalence( const std::unordered_map& hist) { uint64_t total = 0; for (const auto& pair : hist) { total += pair.second; } return GetRecall(hist, total); } // Writes |freq| to |out| sorted by frequency in the following format: // LABEL3 70% // LABEL1 20% // LABEL2 10% // |label_from_key| is used to convert |Key| to label. template void WriteFreq(std::ostream& out, const std::unordered_map& freq, std::string (*label_from_key)(Key), double threshold = 0.001) { std::vector> sorted_freq(freq.begin(), freq.end()); std::sort(sorted_freq.begin(), sorted_freq.end(), [](const std::pair& left, const std::pair& right) { return left.second > right.second; }); for (const auto& pair : sorted_freq) { if (pair.second < threshold) break; out << label_from_key(pair.first) << " " << pair.second * 100.0 << "%" << std::endl; } } // Writes |hist| to |out| sorted by count in the following format: // LABEL3 100 // LABEL1 50 // LABEL2 10 // |label_from_key| is used to convert |Key| to label. template void WriteHist(std::ostream& out, const std::unordered_map& hist, std::string (*label_from_key)(Key)) { std::vector> sorted_hist(hist.begin(), hist.end()); std::sort(sorted_hist.begin(), sorted_hist.end(), [](const std::pair& left, const std::pair& right) { return left.second > right.second; }); for (const auto& pair : sorted_hist) { out << label_from_key(pair.first) << " " << pair.second << std::endl; } } } // namespace StatsAnalyzer::StatsAnalyzer(const SpirvStats& stats) : stats_(stats) { num_modules_ = 0; for (const auto& pair : stats_.version_hist) { num_modules_ += pair.second; } version_freq_ = GetRecall(stats_.version_hist, num_modules_); generator_freq_ = GetRecall(stats_.generator_hist, num_modules_); capability_freq_ = GetRecall(stats_.capability_hist, num_modules_); extension_freq_ = GetRecall(stats_.extension_hist, num_modules_); opcode_freq_ = GetPrevalence(stats_.opcode_hist); } void StatsAnalyzer::WriteVersion(std::ostream& out) { WriteFreq(out, version_freq_, GetVersionString); } void StatsAnalyzer::WriteGenerator(std::ostream& out) { WriteFreq(out, generator_freq_, GetGeneratorString); } void StatsAnalyzer::WriteCapability(std::ostream& out) { WriteFreq(out, capability_freq_, GetCapabilityString); } void StatsAnalyzer::WriteExtension(std::ostream& out) { WriteFreq(out, extension_freq_, KeyIsLabel); } void StatsAnalyzer::WriteOpcode(std::ostream& out) { out << "Total unique opcodes used: " << opcode_freq_.size() << std::endl; WriteFreq(out, opcode_freq_, GetOpcodeString); } void StatsAnalyzer::WriteConstantLiterals(std::ostream& out) { out << "Constant literals" << std::endl; out << "Float 32" << std::endl; WriteFreq(out, GetPrevalence(stats_.f32_constant_hist), KeyIsLabel); out << std::endl << "Float 64" << std::endl; WriteFreq(out, GetPrevalence(stats_.f64_constant_hist), KeyIsLabel); out << std::endl << "Unsigned int 16" << std::endl; WriteFreq(out, GetPrevalence(stats_.u16_constant_hist), KeyIsLabel); out << std::endl << "Signed int 16" << std::endl; WriteFreq(out, GetPrevalence(stats_.s16_constant_hist), KeyIsLabel); out << std::endl << "Unsigned int 32" << std::endl; WriteFreq(out, GetPrevalence(stats_.u32_constant_hist), KeyIsLabel); out << std::endl << "Signed int 32" << std::endl; WriteFreq(out, GetPrevalence(stats_.s32_constant_hist), KeyIsLabel); out << std::endl << "Unsigned int 64" << std::endl; WriteFreq(out, GetPrevalence(stats_.u64_constant_hist), KeyIsLabel); out << std::endl << "Signed int 64" << std::endl; WriteFreq(out, GetPrevalence(stats_.s64_constant_hist), KeyIsLabel); } void StatsAnalyzer::WriteOpcodeMarkov(std::ostream& out) { if (stats_.opcode_markov_hist.empty()) return; const std::unordered_map>& cue_to_hist = stats_.opcode_markov_hist[0]; // Sort by prevalence of the opcodes in opcode_freq_ (descending). std::vector>> sorted_cue_to_hist(cue_to_hist.begin(), cue_to_hist.end()); std::sort(sorted_cue_to_hist.begin(), sorted_cue_to_hist.end(), [this]( const std::pair>& left, const std::pair>& right) { const double lf = opcode_freq_[left.first]; const double rf = opcode_freq_[right.first]; if (lf == rf) return right.first > left.first; return lf > rf; }); for (const auto& kv : sorted_cue_to_hist) { const uint32_t cue = kv.first; const double kFrequentEnoughToAnalyze = 0.0001; if (opcode_freq_[cue] < kFrequentEnoughToAnalyze) continue; const std::unordered_map& hist = kv.second; uint32_t total = 0; for (const auto& pair : hist) { total += pair.second; } std::vector> sorted_hist(hist.begin(), hist.end()); std::sort(sorted_hist.begin(), sorted_hist.end(), [](const std::pair& left, const std::pair& right) { if (left.second == right.second) return right.first > left.first; return left.second > right.second; }); for (const auto& pair : sorted_hist) { const double prior = opcode_freq_[pair.first]; const double posterior = static_cast(pair.second) / static_cast(total); out << GetOpcodeString(cue) << " -> " << GetOpcodeString(pair.first) << " " << posterior * 100 << "% (base rate " << prior * 100 << "%, pair occurrences " << pair.second << ")" << std::endl; } } } void StatsAnalyzer::WriteCodegenOpcodeHist(std::ostream& out) { auto all_opcodes = GetAllOpcodes(); // uint64_t is used because kMarkvNoneOfTheAbove is outside of uint32_t range. out << "std::map GetOpcodeHist() {\n" << " return std::map({\n"; uint32_t total = 0; for (const auto& kv : stats_.opcode_hist) { total += kv.second; } for (uint32_t opcode : all_opcodes) { const auto it = stats_.opcode_hist.find(opcode); const uint32_t count = it == stats_.opcode_hist.end() ? 0 : it->second; const double kMaxValue = 1000.0; uint32_t value = uint32_t(kMaxValue * double(count) / double(total)); if (value == 0) value = 1; out << " { SpvOp" << GetOpcodeString(opcode) << ", " << value << " },\n"; } // Add kMarkvNoneOfTheAbove as a signal for unknown opcode. out << " { kMarkvNoneOfTheAbove, " << 10 << " },\n"; out << " });\n}\n"; } void StatsAnalyzer::WriteCodegenOpcodeAndNumOperandsHist(std::ostream& out) { out << "std::map GetOpcodeAndNumOperandsHist() {\n" << " return std::map({\n"; uint32_t total = 0; for (const auto& kv : stats_.opcode_and_num_operands_hist) { total += kv.second; } uint32_t left_out = 0; for (const auto& kv : stats_.opcode_and_num_operands_hist) { const uint32_t count = kv.second; const double kFrequentEnoughToAnalyze = 0.001; const uint32_t opcode_and_num_operands = kv.first; const uint32_t opcode = opcode_and_num_operands & 0xFFFF; const uint32_t num_operands = opcode_and_num_operands >> 16; if (opcode == SpvOpTypeStruct || double(count) / double(total) < kFrequentEnoughToAnalyze) { left_out += count; continue; } out << " { CombineOpcodeAndNumOperands(SpvOp" << spvOpcodeString(SpvOp(opcode)) << ", " << num_operands << "), " << count << " },\n"; } // Heuristic. const uint32_t none_of_the_above = std::max(1, int(left_out + total * 0.01)); out << " { kMarkvNoneOfTheAbove, " << none_of_the_above << " },\n"; out << " });\n}\n"; } void StatsAnalyzer::WriteCodegenOpcodeAndNumOperandsMarkovHuffmanCodecs( std::ostream& out) { out << "std::map>>\n" << "GetOpcodeAndNumOperandsMarkovHuffmanCodecs() {\n" << " std::map>> " << "codecs;\n"; for (const auto& kv : stats_.opcode_and_num_operands_markov_hist) { const uint32_t prev_opcode = kv.first; const double kFrequentEnoughToAnalyze = 0.001; if (opcode_freq_[prev_opcode] < kFrequentEnoughToAnalyze) continue; const std::unordered_map& hist = kv.second; uint32_t total = 0; for (const auto& pair : hist) { total += pair.second; } uint32_t left_out = 0; std::map processed_hist; for (const auto& pair : hist) { const uint32_t opcode_and_num_operands = pair.first; const uint32_t opcode = opcode_and_num_operands & 0xFFFF; if (opcode == SpvOpTypeStruct) continue; const uint32_t num_operands = opcode_and_num_operands >> 16; const uint32_t count = pair.second; const double posterior_freq = double(count) / double(total); if (opcode_freq_[opcode] < kFrequentEnoughToAnalyze && posterior_freq < kFrequentEnoughToAnalyze) { left_out += count; continue; } processed_hist.emplace(CombineOpcodeAndNumOperands(opcode, num_operands), count); } // Heuristic. processed_hist.emplace(kMarkvNoneOfTheAbove, std::max(1, int(left_out + total * 0.01))); HuffmanCodec codec(processed_hist); out << " {\n"; out << " std::unique_ptr> " << "codec(new HuffmanCodec"; out << codec.SerializeToText(4); out << ");\n" << std::endl; out << " codecs.emplace(SpvOp" << GetOpcodeString(prev_opcode) << ", std::move(codec));\n"; out << " }\n\n"; } out << " return codecs;\n}\n"; } void StatsAnalyzer::WriteCodegenLiteralStringHuffmanCodecs(std::ostream& out) { out << "std::map>>\n" << "GetLiteralStringHuffmanCodecs() {\n" << " std::map>> " << "codecs;\n"; for (const auto& kv : stats_.literal_strings_hist) { const uint32_t opcode = kv.first; if (opcode == SpvOpName || opcode == SpvOpMemberName) continue; const double kOpcodeFrequentEnoughToAnalyze = 0.001; if (opcode_freq_[opcode] < kOpcodeFrequentEnoughToAnalyze) continue; const std::unordered_map& hist = kv.second; uint32_t total = 0; for (const auto& pair : hist) { total += pair.second; } uint32_t left_out = 0; std::map processed_hist; for (const auto& pair : hist) { const uint32_t count = pair.second; const double freq = double(count) / double(total); const double kStringFrequentEnoughToAnalyze = 0.001; if (freq < kStringFrequentEnoughToAnalyze) { left_out += count; continue; } processed_hist.emplace(pair.first, count); } // Heuristic. processed_hist.emplace("kMarkvNoneOfTheAbove", std::max(1, int(left_out + total * 0.01))); HuffmanCodec codec(processed_hist); out << " {\n"; out << " std::unique_ptr> " << "codec(new HuffmanCodec"; out << codec.SerializeToText(4); out << ");\n" << std::endl; out << " codecs.emplace(SpvOp" << spvOpcodeString(SpvOp(opcode)) << ", std::move(codec));\n"; out << " }\n\n"; } out << " return codecs;\n}\n"; } void StatsAnalyzer::WriteCodegenNonIdWordHuffmanCodecs(std::ostream& out) { out << "std::map, " << "std::unique_ptr>>\n" << "GetNonIdWordHuffmanCodecs() {\n" << " std::map, " << "std::unique_ptr>> codecs;\n"; for (const auto& kv : stats_.operand_slot_non_id_words_hist) { const auto& opcode_and_index = kv.first; const uint32_t opcode = opcode_and_index.first; const uint32_t index = opcode_and_index.second; const double kOpcodeFrequentEnoughToAnalyze = 0.001; if (opcode_freq_[opcode] < kOpcodeFrequentEnoughToAnalyze) continue; const std::map& hist = kv.second; uint32_t total = 0; for (const auto& pair : hist) { total += pair.second; } uint32_t left_out = 0; std::map processed_hist; for (const auto& pair : hist) { const uint32_t word = pair.first; const uint32_t count = pair.second; const double freq = double(count) / double(total); const double kWordFrequentEnoughToAnalyze = 0.003; if (freq < kWordFrequentEnoughToAnalyze) { left_out += count; continue; } processed_hist.emplace(word, count); } // Heuristic. processed_hist.emplace(kMarkvNoneOfTheAbove, std::max(1, int(left_out + total * 0.01))); HuffmanCodec codec(processed_hist); out << " {\n"; out << " std::unique_ptr> " << "codec(new HuffmanCodec"; out << codec.SerializeToText(4); out << ");\n" << std::endl; out << " codecs.emplace(std::pair(SpvOp" << spvOpcodeString(SpvOp(opcode)) << ", " << index << "), std::move(codec));\n"; out << " }\n\n"; } out << " return codecs;\n}\n"; } void StatsAnalyzer::WriteCodegenIdDescriptorHuffmanCodecs( std::ostream& out) { out << "std::map, " << "std::unique_ptr>>\n" << "GetIdDescriptorHuffmanCodecs() {\n" << " std::map, " << "std::unique_ptr>> codecs;\n"; std::unordered_set descriptors_with_coding_scheme; for (const auto& kv : stats_.operand_slot_id_descriptor_hist) { const auto& opcode_and_index = kv.first; const uint32_t opcode = opcode_and_index.first; const uint32_t index = opcode_and_index.second; const double kOpcodeFrequentEnoughToAnalyze = 0.003; if (opcode_freq_[opcode] < kOpcodeFrequentEnoughToAnalyze) continue; const std::map& hist = kv.second; uint32_t total = 0; for (const auto& pair : hist) { total += pair.second; } uint32_t left_out = 0; std::map processed_hist; for (const auto& pair : hist) { const uint32_t descriptor = pair.first; const uint32_t count = pair.second; const double freq = double(count) / double(total); const double kDescriptorFrequentEnoughToAnalyze = 0.003; if (freq < kDescriptorFrequentEnoughToAnalyze) { left_out += count; continue; } processed_hist.emplace(descriptor, count); descriptors_with_coding_scheme.insert(descriptor); } // Heuristic. processed_hist.emplace(kMarkvNoneOfTheAbove, std::max(1, int(left_out + total * 0.01))); HuffmanCodec codec(processed_hist); out << " {\n"; out << " std::unique_ptr> " << "codec(new HuffmanCodec"; out << codec.SerializeToText(4); out << ");\n" << std::endl; out << " codecs.emplace(std::pair(SpvOp" << spvOpcodeString(SpvOp(opcode)) << ", " << index << "), std::move(codec));\n"; out << " }\n\n"; } out << " return codecs;\n}\n"; out << "\nstd::unordered_set GetDescriptorsWithCodingScheme() {\n" << " std::unordered_set descriptors_with_coding_scheme = {\n"; for (uint32_t descriptor : descriptors_with_coding_scheme) { out << " " << descriptor << ",\n"; } out << " };\n"; out << " return descriptors_with_coding_scheme;\n}\n"; }