Stats analyzer aggregates OpConstant usage

This commit is contained in:
Andrey Tuganov 2017-05-04 13:35:52 -04:00
parent b4cf371936
commit f5facf842f
7 changed files with 212 additions and 4 deletions

View File

@ -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_;
}

View File

@ -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.

View File

@ -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_;

View File

@ -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

View File

@ -151,5 +151,8 @@ int main(int argc, char** argv) {
out << std::endl;
analyzer.WriteOpcodeMarkov(out);
out << std::endl;
analyzer.WriteConstantLiterals(out);
return 0;
}

View File

@ -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;

View File

@ -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