mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-22 11:40:05 +00:00
Stats analyzer aggregates OpConstant usage
This commit is contained in:
parent
b4cf371936
commit
f5facf842f
@ -73,6 +73,7 @@ class StatsAggregator {
|
||||
ProcessOpcode();
|
||||
ProcessCapability();
|
||||
ProcessExtension();
|
||||
ProcessConstant();
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
@ -108,6 +109,49 @@ class StatsAggregator {
|
||||
}
|
||||
}
|
||||
|
||||
// Collects OpConstant statistics.
|
||||
void ProcessConstant() {
|
||||
const Instruction& inst = GetCurrentInstruction();
|
||||
if (inst.opcode() != SpvOpConstant) return;
|
||||
const uint32_t type_id = inst.GetOperandAs<uint32_t>(0);
|
||||
const auto type_decl_it = vstate_->all_definitions().find(type_id);
|
||||
assert(type_decl_it != vstate_->all_definitions().end());
|
||||
const Instruction& type_decl_inst = *type_decl_it->second;
|
||||
const SpvOp type_op = type_decl_inst.opcode();
|
||||
if (type_op == SpvOpTypeInt) {
|
||||
const uint32_t bit_width = type_decl_inst.GetOperandAs<uint32_t>(1);
|
||||
const uint32_t is_signed = type_decl_inst.GetOperandAs<uint32_t>(2);
|
||||
assert(is_signed == 0 || is_signed == 1);
|
||||
if (bit_width == 16) {
|
||||
if (is_signed)
|
||||
++stats_->s16_constant_hist[inst.GetOperandAs<int16_t>(2)];
|
||||
else
|
||||
++stats_->u16_constant_hist[inst.GetOperandAs<uint16_t>(2)];
|
||||
} else if (bit_width == 32) {
|
||||
if (is_signed)
|
||||
++stats_->s32_constant_hist[inst.GetOperandAs<int32_t>(2)];
|
||||
else
|
||||
++stats_->u32_constant_hist[inst.GetOperandAs<uint32_t>(2)];
|
||||
} else if (bit_width == 64) {
|
||||
if (is_signed)
|
||||
++stats_->s64_constant_hist[inst.GetOperandAs<int64_t>(2)];
|
||||
else
|
||||
++stats_->u64_constant_hist[inst.GetOperandAs<uint64_t>(2)];
|
||||
} else {
|
||||
assert(false && "TypeInt bit width is not 16, 32 or 64");
|
||||
}
|
||||
} else if (type_op == SpvOpTypeFloat) {
|
||||
const uint32_t bit_width = type_decl_inst.GetOperandAs<uint32_t>(1);
|
||||
if (bit_width == 32) {
|
||||
++stats_->f32_constant_hist[inst.GetOperandAs<float>(2)];
|
||||
} else if (bit_width == 64) {
|
||||
++stats_->f64_constant_hist[inst.GetOperandAs<double>(2)];
|
||||
} else {
|
||||
assert(bit_width == 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpirvStats* stats() {
|
||||
return stats_;
|
||||
}
|
||||
|
@ -39,6 +39,30 @@ struct SpirvStats {
|
||||
// Opcode histogram, SpvOpXXX -> count.
|
||||
std::unordered_map<uint32_t, uint32_t> opcode_hist;
|
||||
|
||||
// OpConstant u16 histogram, value -> count.
|
||||
std::unordered_map<uint16_t, uint32_t> u16_constant_hist;
|
||||
|
||||
// OpConstant u32 histogram, value -> count.
|
||||
std::unordered_map<uint32_t, uint32_t> u32_constant_hist;
|
||||
|
||||
// OpConstant u64 histogram, value -> count.
|
||||
std::unordered_map<uint64_t, uint32_t> u64_constant_hist;
|
||||
|
||||
// OpConstant s16 histogram, value -> count.
|
||||
std::unordered_map<int16_t, uint32_t> s16_constant_hist;
|
||||
|
||||
// OpConstant s32 histogram, value -> count.
|
||||
std::unordered_map<int32_t, uint32_t> s32_constant_hist;
|
||||
|
||||
// OpConstant s64 histogram, value -> count.
|
||||
std::unordered_map<int64_t, uint32_t> s64_constant_hist;
|
||||
|
||||
// OpConstant f32 histogram, value -> count.
|
||||
std::unordered_map<float, uint32_t> f32_constant_hist;
|
||||
|
||||
// OpConstant f64 histogram, value -> count.
|
||||
std::unordered_map<double, uint32_t> f64_constant_hist;
|
||||
|
||||
// Used to collect statistics on opcodes triggering other opcodes.
|
||||
// Container scheme: gap between instructions -> cue opcode -> later opcode
|
||||
// -> count.
|
||||
|
@ -15,8 +15,8 @@
|
||||
#ifndef LIBSPIRV_VAL_INSTRUCTION_H_
|
||||
#define LIBSPIRV_VAL_INSTRUCTION_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -76,6 +76,15 @@ class Instruction {
|
||||
return inst_;
|
||||
}
|
||||
|
||||
// Casts the words belonging to the operand under |index| to |T| and returns.
|
||||
template <typename T>
|
||||
T GetOperandAs(size_t index) const {
|
||||
const spv_parsed_operand_t& operand = operands_.at(index);
|
||||
assert(operand.num_words * 4 >= sizeof(T));
|
||||
assert(operand.offset + operand.num_words <= inst_.num_words);
|
||||
return *reinterpret_cast<const T*>(&words_[operand.offset]);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<uint32_t> words_;
|
||||
const std::vector<spv_parsed_operand_t> operands_;
|
||||
|
@ -339,4 +339,98 @@ OpMemoryModel Physical32 OpenCL
|
||||
1u, stats.opcode_markov_hist[1].at(SpvOpTypeInt).at(SpvOpTypeFloat));
|
||||
}
|
||||
|
||||
TEST(AggregateStats, ConstantLiteralsHistogram) {
|
||||
const std::string code1 = R"(
|
||||
OpCapability Addresses
|
||||
OpCapability Kernel
|
||||
OpCapability GenericPointer
|
||||
OpCapability Linkage
|
||||
OpCapability Float64
|
||||
OpCapability Int16
|
||||
OpCapability Int64
|
||||
OpMemoryModel Physical32 OpenCL
|
||||
%u16 = OpTypeInt 16 0
|
||||
%u32 = OpTypeInt 32 0
|
||||
%u64 = OpTypeInt 64 0
|
||||
%f32 = OpTypeFloat 32
|
||||
%f64 = OpTypeFloat 64
|
||||
%1 = OpConstant %f32 0.1
|
||||
%2 = OpConstant %f32 -2
|
||||
%3 = OpConstant %f64 -2
|
||||
%4 = OpConstant %u16 16
|
||||
%5 = OpConstant %u16 2
|
||||
%6 = OpConstant %u32 32
|
||||
%7 = OpConstant %u64 64
|
||||
)";
|
||||
|
||||
const std::string code2 = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability Int16
|
||||
OpCapability Int64
|
||||
OpMemoryModel Logical GLSL450
|
||||
%f32 = OpTypeFloat 32
|
||||
%u16 = OpTypeInt 16 0
|
||||
%s16 = OpTypeInt 16 1
|
||||
%u32 = OpTypeInt 32 0
|
||||
%s32 = OpTypeInt 32 1
|
||||
%u64 = OpTypeInt 64 0
|
||||
%s64 = OpTypeInt 64 1
|
||||
%1 = OpConstant %f32 0.1
|
||||
%2 = OpConstant %f32 -2
|
||||
%3 = OpConstant %u16 1
|
||||
%4 = OpConstant %u16 16
|
||||
%5 = OpConstant %u16 2
|
||||
%6 = OpConstant %s16 -16
|
||||
%7 = OpConstant %u32 32
|
||||
%8 = OpConstant %s32 2
|
||||
%9 = OpConstant %s32 -32
|
||||
%10 = OpConstant %u64 64
|
||||
%11 = OpConstant %s64 -64
|
||||
)";
|
||||
|
||||
SpirvStats stats;
|
||||
|
||||
CompileAndAggregateStats(code1, &stats);
|
||||
EXPECT_EQ(2u, stats.f32_constant_hist.size());
|
||||
EXPECT_EQ(1u, stats.f64_constant_hist.size());
|
||||
EXPECT_EQ(1u, stats.f32_constant_hist.at(0.1f));
|
||||
EXPECT_EQ(1u, stats.f32_constant_hist.at(-2.f));
|
||||
EXPECT_EQ(1u, stats.f64_constant_hist.at(-2));
|
||||
|
||||
EXPECT_EQ(2u, stats.u16_constant_hist.size());
|
||||
EXPECT_EQ(0u, stats.s16_constant_hist.size());
|
||||
EXPECT_EQ(1u, stats.u32_constant_hist.size());
|
||||
EXPECT_EQ(0u, stats.s32_constant_hist.size());
|
||||
EXPECT_EQ(1u, stats.u64_constant_hist.size());
|
||||
EXPECT_EQ(0u, stats.s64_constant_hist.size());
|
||||
EXPECT_EQ(1u, stats.u16_constant_hist.at(16));
|
||||
EXPECT_EQ(1u, stats.u16_constant_hist.at(2));
|
||||
EXPECT_EQ(1u, stats.u32_constant_hist.at(32));
|
||||
EXPECT_EQ(1u, stats.u64_constant_hist.at(64));
|
||||
|
||||
CompileAndAggregateStats(code2, &stats);
|
||||
EXPECT_EQ(2u, stats.f32_constant_hist.size());
|
||||
EXPECT_EQ(1u, stats.f64_constant_hist.size());
|
||||
EXPECT_EQ(2u, stats.f32_constant_hist.at(0.1f));
|
||||
EXPECT_EQ(2u, stats.f32_constant_hist.at(-2.f));
|
||||
EXPECT_EQ(1u, stats.f64_constant_hist.at(-2));
|
||||
|
||||
EXPECT_EQ(3u, stats.u16_constant_hist.size());
|
||||
EXPECT_EQ(1u, stats.s16_constant_hist.size());
|
||||
EXPECT_EQ(1u, stats.u32_constant_hist.size());
|
||||
EXPECT_EQ(2u, stats.s32_constant_hist.size());
|
||||
EXPECT_EQ(1u, stats.u64_constant_hist.size());
|
||||
EXPECT_EQ(1u, stats.s64_constant_hist.size());
|
||||
EXPECT_EQ(2u, stats.u16_constant_hist.at(16));
|
||||
EXPECT_EQ(2u, stats.u16_constant_hist.at(2));
|
||||
EXPECT_EQ(1u, stats.u16_constant_hist.at(1));
|
||||
EXPECT_EQ(1u, stats.s16_constant_hist.at(-16));
|
||||
EXPECT_EQ(2u, stats.u32_constant_hist.at(32));
|
||||
EXPECT_EQ(1u, stats.s32_constant_hist.at(2));
|
||||
EXPECT_EQ(1u, stats.s32_constant_hist.at(-32));
|
||||
EXPECT_EQ(2u, stats.u64_constant_hist.at(64));
|
||||
EXPECT_EQ(1u, stats.s64_constant_hist.at(-64));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -151,5 +151,8 @@ int main(int argc, char** argv) {
|
||||
out << std::endl;
|
||||
analyzer.WriteOpcodeMarkov(out);
|
||||
|
||||
out << std::endl;
|
||||
analyzer.WriteConstantLiterals(out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -47,8 +47,11 @@ std::string GetCapabilityString(uint32_t word) {
|
||||
return libspirv::CapabilityToString(static_cast<SpvCapability>(word));
|
||||
}
|
||||
|
||||
std::string KeyIsLabel(std::string key) {
|
||||
return key;
|
||||
template <class T>
|
||||
std::string KeyIsLabel(T key) {
|
||||
std::stringstream ss;
|
||||
ss << key;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <class Key>
|
||||
@ -81,7 +84,7 @@ std::unordered_map<Key, double> GetPrevalence(
|
||||
// |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::string (*label_from_key)(Key), double threshold = 0.001) {
|
||||
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,
|
||||
@ -90,6 +93,8 @@ void WriteFreq(std::ostream& out, const std::unordered_map<Key, double>& freq,
|
||||
});
|
||||
|
||||
for (const auto& pair : sorted_freq) {
|
||||
if (pair.second < threshold)
|
||||
break;
|
||||
out << label_from_key(pair.first) << " " << pair.second * 100.0
|
||||
<< "%" << std::endl;
|
||||
}
|
||||
@ -151,6 +156,34 @@ void StatsAnalyzer::WriteOpcode(std::ostream& out) {
|
||||
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;
|
||||
|
@ -29,6 +29,7 @@ class StatsAnalyzer {
|
||||
void WriteCapability(std::ostream& out);
|
||||
void WriteExtension(std::ostream& out);
|
||||
void WriteOpcode(std::ostream& out);
|
||||
void WriteConstantLiterals(std::ostream& out);
|
||||
|
||||
// Writes first order Markov analysis to |out|.
|
||||
// stats_.opcode_markov_hist needs to contain raw data for at least one
|
||||
|
Loading…
Reference in New Issue
Block a user