| // 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 "tools/stats/stats_analyzer.h" | |
| #include <algorithm> | |
| #include <cassert> | |
| #include <cstring> | |
| #include <iostream> | |
| #include <map> | |
| #include <sstream> | |
| #include <string> | |
| #include <unordered_map> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <vector> | |
| #include "source/comp/markv_model.h" | |
| #include "source/enum_string_mapping.h" | |
| #include "source/latest_version_spirv_header.h" | |
| #include "source/opcode.h" | |
| #include "source/operand.h" | |
| #include "source/spirv_constant.h" | |
| namespace spvtools { | |
| namespace stats { | |
| namespace { | |
| // Signals that the value is not in the coding scheme and a fallback method | |
| // needs to be used. | |
| const uint64_t kMarkvNoneOfTheAbove = | |
| comp::MarkvModel::GetMarkvNoneOfTheAbove(); | |
| 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<SpvOp>(word)); | |
| } | |
| std::string GetCapabilityString(uint32_t word) { | |
| return CapabilityToString(static_cast<SpvCapability>(word)); | |
| } | |
| template <class T> | |
| std::string KeyIsLabel(T key) { | |
| std::stringstream ss; | |
| ss << key; | |
| return ss.str(); | |
| } | |
| template <class Key> | |
| std::unordered_map<Key, double> GetRecall( | |
| const std::unordered_map<Key, uint32_t>& hist, uint64_t total) { | |
| std::unordered_map<Key, double> freq; | |
| for (const auto& pair : hist) { | |
| const double frequency = | |
| static_cast<double>(pair.second) / static_cast<double>(total); | |
| freq.emplace(pair.first, frequency); | |
| } | |
| return freq; | |
| } | |
| template <class Key> | |
| std::unordered_map<Key, double> GetPrevalence( | |
| const std::unordered_map<Key, uint32_t>& 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 <class Key> | |
| void WriteFreq(std::ostream& out, const std::unordered_map<Key, double>& freq, | |
| std::string (*label_from_key)(Key)) { | |
| std::vector<std::pair<Key, double>> sorted_freq(freq.begin(), freq.end()); | |
| std::sort(sorted_freq.begin(), sorted_freq.end(), | |
| [](const std::pair<Key, double>& left, | |
| const std::pair<Key, double>& right) { | |
| return left.second > right.second; | |
| }); | |
| for (const auto& pair : sorted_freq) { | |
| if (pair.second < 0.001) break; | |
| out << label_from_key(pair.first) << " " << pair.second * 100.0 << "%" | |
| << 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<uint32_t, std::unordered_map<uint32_t, uint32_t>>& | |
| cue_to_hist = stats_.opcode_markov_hist[0]; | |
| // Sort by prevalence of the opcodes in opcode_freq_ (descending). | |
| std::vector<std::pair<uint32_t, std::unordered_map<uint32_t, uint32_t>>> | |
| 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<uint32_t, std::unordered_map<uint32_t, uint32_t>>& | |
| left, | |
| const std::pair<uint32_t, std::unordered_map<uint32_t, uint32_t>>& | |
| 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<uint32_t, uint32_t>& hist = kv.second; | |
| uint32_t total = 0; | |
| for (const auto& pair : hist) { | |
| total += pair.second; | |
| } | |
| std::vector<std::pair<uint32_t, uint32_t>> sorted_hist(hist.begin(), | |
| hist.end()); | |
| std::sort(sorted_hist.begin(), sorted_hist.end(), | |
| [](const std::pair<uint32_t, uint32_t>& left, | |
| const std::pair<uint32_t, uint32_t>& 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<double>(pair.second) / static_cast<double>(total); | |
| out << GetOpcodeString(cue) << " -> " << GetOpcodeString(pair.first) | |
| << " " << posterior * 100 << "% (base rate " << prior * 100 | |
| << "%, pair occurrences " << pair.second << ")" << std::endl; | |
| } | |
| } | |
| } | |
| } // namespace stats | |
| } // namespace spvtools |