Added statistical analysis tool (tool/stats)

Currently analyzes percentages of the following:
- version (how many 1.0 or 1.1)
- generator
- extension
- capability
- opcode prevalence
This commit is contained in:
Andrey Tuganov 2017-04-06 16:55:26 -04:00 committed by David Neto
parent 72debb8fd4
commit 4f216402ba
11 changed files with 908 additions and 1 deletions

View File

@ -235,6 +235,7 @@ set(SPIRV_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/print.cpp
${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_stats.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.cpp
${CMAKE_CURRENT_SOURCE_DIR}/table.cpp

113
source/spirv_stats.cpp Normal file
View File

@ -0,0 +1,113 @@
// 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 "spirv_stats.h"
#include <cassert>
#include <algorithm>
#include <string>
#include <vector>
#include "binary.h"
#include "diagnostic.h"
#include "enum_string_mapping.h"
#include "extensions.h"
#include "instruction.h"
#include "opcode.h"
#include "operand.h"
#include "spirv-tools/libspirv.h"
#include "spirv_endian.h"
using libspirv::SpirvStats;
namespace {
// Collects statistics from SPIR-V header (version, generator).
spv_result_t ProcessHeader(
void* user_data, spv_endianness_t /* endian */, uint32_t /* magic */,
uint32_t version, uint32_t generator, uint32_t /* id_bound */,
uint32_t /* schema */) {
SpirvStats* stats =
reinterpret_cast<libspirv::SpirvStats*>(user_data);
++stats->version_hist[version];
++stats->generator_hist[generator];
return SPV_SUCCESS;
}
// Collects OpCapability statistics.
void ProcessCapability(SpirvStats* stats,
const spv_parsed_instruction_t* inst) {
if (static_cast<SpvOp>(inst->opcode) != SpvOpCapability) return;
assert(inst->num_operands == 1);
const spv_parsed_operand_t& operand = inst->operands[0];
assert(operand.num_words == 1);
assert(operand.offset < inst->num_words);
const uint32_t capability = inst->words[operand.offset];
++stats->capability_hist[capability];
}
// Collects OpExtension statistics.
void ProcessExtension(SpirvStats* stats,
const spv_parsed_instruction_t* inst) {
if (static_cast<SpvOp>(inst->opcode) != SpvOpExtension) return;
const std::string extension = libspirv::GetExtensionString(inst);
++stats->extension_hist[extension];
}
// Collects opcode usage statistics and calls other collectors.
spv_result_t ProcessInstruction(
void* user_data, const spv_parsed_instruction_t* inst) {
SpirvStats* stats =
reinterpret_cast<libspirv::SpirvStats*>(user_data);
const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
++stats->opcode_hist[opcode];
ProcessCapability(stats, inst);
ProcessExtension(stats, inst);
return SPV_SUCCESS;
}
} // namespace
namespace libspirv {
spv_result_t AggregateStats(
const spv_context_t& context, const uint32_t* words, const size_t num_words,
spv_diagnostic* pDiagnostic, SpirvStats* stats) {
spv_const_binary_t binary = {words, num_words};
spv_endianness_t endian;
spv_position_t position = {};
if (spvBinaryEndianness(&binary, &endian)) {
return libspirv::DiagnosticStream(position, context.consumer,
SPV_ERROR_INVALID_BINARY)
<< "Invalid SPIR-V magic number.";
}
spv_header_t header;
if (spvBinaryHeaderGet(&binary, endian, &header)) {
return libspirv::DiagnosticStream(position, context.consumer,
SPV_ERROR_INVALID_BINARY)
<< "Invalid SPIR-V header.";
}
return spvBinaryParse(&context, stats, words, num_words,
ProcessHeader, ProcessInstruction,
pDiagnostic);
}
} // namespace libspirv

50
source/spirv_stats.h Normal file
View File

@ -0,0 +1,50 @@
// 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.
#ifndef LIBSPIRV_SPIRV_STATS_H_
#define LIBSPIRV_SPIRV_STATS_H_
#include <string>
#include <unordered_map>
#include <vector>
#include "spirv-tools/libspirv.hpp"
namespace libspirv {
struct SpirvStats {
// Version histogram, version_word -> count.
std::unordered_map<uint32_t, uint32_t> version_hist;
// Generator histogram, generator_word -> count.
std::unordered_map<uint32_t, uint32_t> generator_hist;
// Capability histogram, SpvCapabilityXXX -> count.
std::unordered_map<uint32_t, uint32_t> capability_hist;
// Extension histogram, extension_string -> count.
std::unordered_map<std::string, uint32_t> extension_hist;
// Opcode histogram, SpvOpXXX -> count.
std::unordered_map<uint32_t, uint32_t> opcode_hist;
};
// Aggregates existing |stats| with new stats extracted from |binary|.
spv_result_t AggregateStats(
const spv_context_t& context, const uint32_t* words, const size_t num_words,
spv_diagnostic* pDiagnostic, SpirvStats* stats);
} // namespace libspirv
#endif // LIBSPIRV_SPIRV_STATS_H_

View File

@ -161,3 +161,4 @@ add_spvtools_unittest(
add_subdirectory(opt)
add_subdirectory(val)
add_subdirectory(stats)

31
test/stats/CMakeLists.txt Normal file
View File

@ -0,0 +1,31 @@
# 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.
set(VAL_TEST_COMMON_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/../test_fixture.h
${CMAKE_CURRENT_SOURCE_DIR}/../unit_spirv.h
)
add_spvtools_unittest(TARGET stats_aggregate
SRCS stats_aggregate_test.cpp
${VAL_TEST_COMMON_SRCS}
LIBS ${SPIRV_TOOLS}
)
add_spvtools_unittest(TARGET stats_analyzer
SRCS stats_analyzer_test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../tools/stats/stats_analyzer.cpp
${VAL_TEST_COMMON_SRCS}
LIBS ${SPIRV_TOOLS}
)

View File

@ -0,0 +1,238 @@
// 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.
// Tests for unique type declaration rules validator.
#include <string>
#include "source/spirv_stats.h"
#include "test_fixture.h"
#include "unit_spirv.h"
namespace {
using libspirv::SpirvStats;
using spvtest::ScopedContext;
// Calls libspirv::AggregateStats for binary compiled from |code|.
void CompileAndAggregateStats(const std::string& code, SpirvStats* stats,
spv_target_env env = SPV_ENV_UNIVERSAL_1_1) {
ScopedContext ctx(env);
spv_binary binary;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(
ctx.context, code.c_str(), code.size(), &binary, nullptr));
ASSERT_EQ(SPV_SUCCESS, AggregateStats(*ctx.context, binary->code,
binary->wordCount, nullptr, stats));
spvBinaryDestroy(binary);
}
TEST(AggregateStats, CapabilityHistogram) {
const std::string code1 = R"(
OpCapability Addresses
OpCapability Kernel
OpCapability GenericPointer
OpCapability Linkage
OpMemoryModel Physical32 OpenCL
)";
const std::string code2 = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
)";
SpirvStats stats;
CompileAndAggregateStats(code1, &stats);
EXPECT_EQ(4u, stats.capability_hist.size());
EXPECT_EQ(0u, stats.capability_hist.count(SpvCapabilityShader));
EXPECT_EQ(1u, stats.capability_hist.at(SpvCapabilityAddresses));
EXPECT_EQ(1u, stats.capability_hist.at(SpvCapabilityKernel));
EXPECT_EQ(1u, stats.capability_hist.at(SpvCapabilityGenericPointer));
EXPECT_EQ(1u, stats.capability_hist.at(SpvCapabilityLinkage));
CompileAndAggregateStats(code2, &stats);
EXPECT_EQ(5u, stats.capability_hist.size());
EXPECT_EQ(1u, stats.capability_hist.at(SpvCapabilityShader));
EXPECT_EQ(1u, stats.capability_hist.at(SpvCapabilityAddresses));
EXPECT_EQ(1u, stats.capability_hist.at(SpvCapabilityKernel));
EXPECT_EQ(1u, stats.capability_hist.at(SpvCapabilityGenericPointer));
EXPECT_EQ(2u, stats.capability_hist.at(SpvCapabilityLinkage));
CompileAndAggregateStats(code1, &stats);
EXPECT_EQ(5u, stats.capability_hist.size());
EXPECT_EQ(1u, stats.capability_hist.at(SpvCapabilityShader));
EXPECT_EQ(2u, stats.capability_hist.at(SpvCapabilityAddresses));
EXPECT_EQ(2u, stats.capability_hist.at(SpvCapabilityKernel));
EXPECT_EQ(2u, stats.capability_hist.at(SpvCapabilityGenericPointer));
EXPECT_EQ(3u, stats.capability_hist.at(SpvCapabilityLinkage));
CompileAndAggregateStats(code2, &stats);
EXPECT_EQ(5u, stats.capability_hist.size());
EXPECT_EQ(2u, stats.capability_hist.at(SpvCapabilityShader));
EXPECT_EQ(2u, stats.capability_hist.at(SpvCapabilityAddresses));
EXPECT_EQ(2u, stats.capability_hist.at(SpvCapabilityKernel));
EXPECT_EQ(2u, stats.capability_hist.at(SpvCapabilityGenericPointer));
EXPECT_EQ(4u, stats.capability_hist.at(SpvCapabilityLinkage));
}
TEST(AggregateStats, ExtensionHistogram) {
const std::string code1 = R"(
OpCapability Addresses
OpCapability Kernel
OpCapability GenericPointer
OpCapability Linkage
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Physical32 OpenCL
)";
const std::string code2 = R"(
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_NV_viewport_array2"
OpExtension "greatest_extension_ever"
OpMemoryModel Logical GLSL450
)";
SpirvStats stats;
CompileAndAggregateStats(code1, &stats);
EXPECT_EQ(1u, stats.extension_hist.size());
EXPECT_EQ(0u, stats.extension_hist.count("SPV_NV_viewport_array2"));
EXPECT_EQ(1u, stats.extension_hist.at("SPV_KHR_16bit_storage"));
CompileAndAggregateStats(code2, &stats);
EXPECT_EQ(3u, stats.extension_hist.size());
EXPECT_EQ(1u, stats.extension_hist.at("SPV_NV_viewport_array2"));
EXPECT_EQ(1u, stats.extension_hist.at("SPV_KHR_16bit_storage"));
EXPECT_EQ(1u, stats.extension_hist.at("greatest_extension_ever"));
CompileAndAggregateStats(code1, &stats);
EXPECT_EQ(3u, stats.extension_hist.size());
EXPECT_EQ(1u, stats.extension_hist.at("SPV_NV_viewport_array2"));
EXPECT_EQ(2u, stats.extension_hist.at("SPV_KHR_16bit_storage"));
EXPECT_EQ(1u, stats.extension_hist.at("greatest_extension_ever"));
CompileAndAggregateStats(code2, &stats);
EXPECT_EQ(3u, stats.extension_hist.size());
EXPECT_EQ(2u, stats.extension_hist.at("SPV_NV_viewport_array2"));
EXPECT_EQ(2u, stats.extension_hist.at("SPV_KHR_16bit_storage"));
EXPECT_EQ(2u, stats.extension_hist.at("greatest_extension_ever"));
}
TEST(AggregateStats, VersionHistogram) {
const std::string code1 = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
)";
SpirvStats stats;
CompileAndAggregateStats(code1, &stats);
EXPECT_EQ(1u, stats.version_hist.size());
EXPECT_EQ(1u, stats.version_hist.at(0x00010100));
CompileAndAggregateStats(code1, &stats, SPV_ENV_UNIVERSAL_1_0);
EXPECT_EQ(2u, stats.version_hist.size());
EXPECT_EQ(1u, stats.version_hist.at(0x00010100));
EXPECT_EQ(1u, stats.version_hist.at(0x00010000));
CompileAndAggregateStats(code1, &stats);
EXPECT_EQ(2u, stats.version_hist.size());
EXPECT_EQ(2u, stats.version_hist.at(0x00010100));
EXPECT_EQ(1u, stats.version_hist.at(0x00010000));
CompileAndAggregateStats(code1, &stats, SPV_ENV_UNIVERSAL_1_0);
EXPECT_EQ(2u, stats.version_hist.size());
EXPECT_EQ(2u, stats.version_hist.at(0x00010100));
EXPECT_EQ(2u, stats.version_hist.at(0x00010000));
}
TEST(AggregateStats, GeneratorHistogram) {
const std::string code1 = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
)";
const uint32_t kGeneratorKhronosAssembler =
SPV_GENERATOR_KHRONOS_ASSEMBLER << 16;
SpirvStats stats;
CompileAndAggregateStats(code1, &stats);
EXPECT_EQ(1u, stats.generator_hist.size());
EXPECT_EQ(1u, stats.generator_hist.at(kGeneratorKhronosAssembler));
CompileAndAggregateStats(code1, &stats);
EXPECT_EQ(1u, stats.generator_hist.size());
EXPECT_EQ(2u, stats.generator_hist.at(kGeneratorKhronosAssembler));
}
TEST(AggregateStats, OpcodeHistogram) {
const std::string code1 = R"(
OpCapability Addresses
OpCapability Kernel
OpCapability GenericPointer
OpCapability Linkage
OpMemoryModel Physical32 OpenCL
%i32 = OpTypeInt 32 1
%u32 = OpTypeInt 32 0
%f32 = OpTypeFloat 32
)";
const std::string code2 = R"(
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_NV_viewport_array2"
OpMemoryModel Logical GLSL450
)";
SpirvStats stats;
CompileAndAggregateStats(code1, &stats);
EXPECT_EQ(4u, stats.opcode_hist.size());
EXPECT_EQ(4u, stats.opcode_hist.at(SpvOpCapability));
EXPECT_EQ(1u, stats.opcode_hist.at(SpvOpMemoryModel));
EXPECT_EQ(2u, stats.opcode_hist.at(SpvOpTypeInt));
EXPECT_EQ(1u, stats.opcode_hist.at(SpvOpTypeFloat));
CompileAndAggregateStats(code2, &stats);
EXPECT_EQ(5u, stats.opcode_hist.size());
EXPECT_EQ(6u, stats.opcode_hist.at(SpvOpCapability));
EXPECT_EQ(2u, stats.opcode_hist.at(SpvOpMemoryModel));
EXPECT_EQ(2u, stats.opcode_hist.at(SpvOpTypeInt));
EXPECT_EQ(1u, stats.opcode_hist.at(SpvOpTypeFloat));
EXPECT_EQ(1u, stats.opcode_hist.at(SpvOpExtension));
CompileAndAggregateStats(code1, &stats);
EXPECT_EQ(5u, stats.opcode_hist.size());
EXPECT_EQ(10u, stats.opcode_hist.at(SpvOpCapability));
EXPECT_EQ(3u, stats.opcode_hist.at(SpvOpMemoryModel));
EXPECT_EQ(4u, stats.opcode_hist.at(SpvOpTypeInt));
EXPECT_EQ(2u, stats.opcode_hist.at(SpvOpTypeFloat));
EXPECT_EQ(1u, stats.opcode_hist.at(SpvOpExtension));
CompileAndAggregateStats(code2, &stats);
EXPECT_EQ(5u, stats.opcode_hist.size());
EXPECT_EQ(12u, stats.opcode_hist.at(SpvOpCapability));
EXPECT_EQ(4u, stats.opcode_hist.at(SpvOpMemoryModel));
EXPECT_EQ(4u, stats.opcode_hist.at(SpvOpTypeInt));
EXPECT_EQ(2u, stats.opcode_hist.at(SpvOpTypeFloat));
EXPECT_EQ(2u, stats.opcode_hist.at(SpvOpExtension));
}
} // namespace

View File

@ -0,0 +1,143 @@
// 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.
// Tests for unique type declaration rules validator.
#include <string>
#include <sstream>
#include "spirv/1.1/spirv.h"
#include "test_fixture.h"
#include "tools/stats/stats_analyzer.h"
namespace {
using libspirv::SpirvStats;
// Fills |stats| with some synthetic header stats, as if aggregated from 100
// modules (100 used for simpler percentage evaluation).
void FillDefaultStats(SpirvStats* stats) {
*stats = SpirvStats();
stats->version_hist[0x00010000] = 40;
stats->version_hist[0x00010100] = 60;
stats->generator_hist[0x00000000] = 64;
stats->generator_hist[0x00010000] = 1;
stats->generator_hist[0x00020000] = 2;
stats->generator_hist[0x00030000] = 3;
stats->generator_hist[0x00040000] = 4;
stats->generator_hist[0x00050000] = 5;
stats->generator_hist[0x00060000] = 6;
stats->generator_hist[0x00070000] = 7;
stats->generator_hist[0x00080000] = 8;
int num_version_entries = 0;
for (const auto& pair : stats->version_hist) {
num_version_entries += pair.second;
}
int num_generator_entries = 0;
for (const auto& pair : stats->generator_hist) {
num_generator_entries += pair.second;
}
EXPECT_EQ(num_version_entries, num_generator_entries);
}
TEST(StatsAnalyzer, Version) {
SpirvStats stats;
FillDefaultStats(&stats);
StatsAnalyzer analyzer(stats);
std::stringstream ss;
analyzer.WriteVersion(ss);
const std::string output = ss.str();
const std::string expected_output = "Version 1.1 60%\nVersion 1.0 40%\n";
EXPECT_EQ(expected_output, output);
}
TEST(StatsAnalyzer, Generator) {
SpirvStats stats;
FillDefaultStats(&stats);
StatsAnalyzer analyzer(stats);
std::stringstream ss;
analyzer.WriteGenerator(ss);
const std::string output = ss.str();
const std::string expected_output =
"Khronos 64%\nKhronos Glslang Reference Front End 8%\n"
"Khronos SPIR-V Tools Assembler 7%\nKhronos LLVM/SPIR-V Translator 6%"
"\nARM 5%\nNVIDIA 4%\nCodeplay 3%\nValve 2%\nLunarG 1%\n";
EXPECT_EQ(expected_output, output);
}
TEST(StatsAnalyzer, Capability) {
SpirvStats stats;
FillDefaultStats(&stats);
stats.capability_hist[SpvCapabilityShader] = 25;
stats.capability_hist[SpvCapabilityKernel] = 75;
StatsAnalyzer analyzer(stats);
std::stringstream ss;
analyzer.WriteCapability(ss);
const std::string output = ss.str();
const std::string expected_output = "Kernel 75%\nShader 25%\n";
EXPECT_EQ(expected_output, output);
}
TEST(StatsAnalyzer, Extension) {
SpirvStats stats;
FillDefaultStats(&stats);
stats.extension_hist["greatest_extension_ever"] = 1;
stats.extension_hist["worst_extension_ever"] = 10;
StatsAnalyzer analyzer(stats);
std::stringstream ss;
analyzer.WriteExtension(ss);
const std::string output = ss.str();
const std::string expected_output =
"worst_extension_ever 10%\ngreatest_extension_ever 1%\n";
EXPECT_EQ(expected_output, output);
}
TEST(StatsAnalyzer, Opcode) {
SpirvStats stats;
FillDefaultStats(&stats);
stats.opcode_hist[SpvOpCapability] = 20;
stats.opcode_hist[SpvOpConstant] = 80;
stats.opcode_hist[SpvOpDecorate] = 100;
StatsAnalyzer analyzer(stats);
std::stringstream ss;
analyzer.WriteOpcode(ss);
const std::string output = ss.str();
const std::string expected_output =
"Total unique opcodes used: 3\nDecorate 50%\n"
"Constant 40%\nCapability 10%\n";
EXPECT_EQ(expected_output, output);
}
} // namespace

View File

@ -42,6 +42,10 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
add_spvtools_tool(TARGET spirv-dis SRCS dis/dis.cpp LIBS ${SPIRV_TOOLS})
add_spvtools_tool(TARGET spirv-val SRCS val/val.cpp LIBS ${SPIRV_TOOLS})
add_spvtools_tool(TARGET spirv-opt SRCS opt/opt.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS})
add_spvtools_tool(TARGET spirv-stats
SRCS stats/stats.cpp
stats/stats_analyzer.cpp
LIBS ${SPIRV_TOOLS})
add_spvtools_tool(TARGET spirv-cfg
SRCS cfg/cfg.cpp
cfg/bin_to_dot.h
@ -49,8 +53,10 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
LIBS ${SPIRV_TOOLS})
target_include_directories(spirv-cfg PRIVATE ${spirv-tools_SOURCE_DIR}
${SPIRV_HEADER_INCLUDE_DIR})
target_include_directories(spirv-stats PRIVATE ${spirv-tools_SOURCE_DIR}
${SPIRV_HEADER_INCLUDE_DIR})
set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt spirv-cfg)
set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt spirv-stats spirv-cfg)
install(TARGETS ${SPIRV_INSTALL_TARGETS}
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib

152
tools/stats/stats.cpp Normal file
View File

@ -0,0 +1,152 @@
// 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 <cassert>
#include <cstring>
#include <iostream>
#include <unordered_map>
#include "source/spirv_stats.h"
#include "source/table.h"
#include "spirv-tools/libspirv.h"
#include "stats_analyzer.h"
#include "tools/io.h"
using libspirv::SpirvStats;
namespace {
struct ScopedContext {
ScopedContext(spv_target_env env) : context(spvContextCreate(env)) {}
~ScopedContext() { spvContextDestroy(context); }
spv_context context;
};
void PrintUsage(char* argv0) {
printf(
R"(%s - Collect statistics from one or more SPIR-V binary file(s).
USAGE: %s [options] [<filepaths>]
TIP: In order to collect statistics from all .spv files under current dir use
find . -name "*.spv" -print0 | xargs -0 -s 2000000 %s
Options:
-h, --help Print this help.
-v, --verbose Print additional info to stderr.
)",
argv0, argv0, argv0);
}
void DiagnosticsMessageHandler(spv_message_level_t level, const char*,
const spv_position_t& position,
const char* message) {
switch (level) {
case SPV_MSG_FATAL:
case SPV_MSG_INTERNAL_ERROR:
case SPV_MSG_ERROR:
std::cerr << "error: " << position.index << ": " << message
<< std::endl;
break;
case SPV_MSG_WARNING:
std::cout << "warning: " << position.index << ": " << message
<< std::endl;
break;
case SPV_MSG_INFO:
std::cout << "info: " << position.index << ": " << message << std::endl;
break;
default:
break;
}
}
} // namespace
int main(int argc, char** argv) {
bool continue_processing = true;
int return_code = 0;
bool verbose = false;
std::vector<const char*> paths;
for (int argi = 1; continue_processing && argi < argc; ++argi) {
const char* cur_arg = argv[argi];
if ('-' == cur_arg[0]) {
if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
PrintUsage(argv[0]);
continue_processing = false;
return_code = 0;
} else if (0 == strcmp(cur_arg, "--verbose") || 0 == strcmp(cur_arg, "-v")) {
verbose = true;
} else {
PrintUsage(argv[0]);
continue_processing = false;
return_code = 1;
}
} else {
paths.push_back(cur_arg);
}
}
// Exit if command line parsing was not successful.
if (!continue_processing) {
return return_code;
}
std::cerr << "Processing " << paths.size()
<< " files..." << std::endl;
ScopedContext ctx(SPV_ENV_UNIVERSAL_1_1);
SetContextMessageConsumer(ctx.context, DiagnosticsMessageHandler);
libspirv::SpirvStats stats;
for (size_t index = 0; index < paths.size(); ++index) {
const size_t kMilestonePeriod = 1000;
if (verbose) {
if (index % kMilestonePeriod == kMilestonePeriod - 1)
std::cerr << "Processed " << index + 1 << " files..." << std::endl;
}
const char* path = paths[index];
std::vector<uint32_t> contents;
if (!ReadFile<uint32_t>(path, "rb", &contents)) return 1;
if (SPV_SUCCESS != libspirv::AggregateStats(
*ctx.context, contents.data(), contents.size(), nullptr, &stats)) {
std::cerr << "error: Failed to aggregate stats for " << path << std::endl;
return 1;
}
}
StatsAnalyzer analyzer(stats);
std::ostream& out = std::cout;
out << std::endl;
analyzer.WriteVersion(out);
analyzer.WriteGenerator(out);
out << std::endl;
analyzer.WriteCapability(out);
out << std::endl;
analyzer.WriteExtension(out);
out << std::endl;
analyzer.WriteOpcode(out);
return 0;
}

View File

@ -0,0 +1,127 @@
// 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 <algorithm>
#include <iostream>
#include <sstream>
#include <vector>
#include "source/enum_string_mapping.h"
#include "source/opcode.h"
#include "source/spirv_constant.h"
#include "spirv/1.1/spirv.h"
using libspirv::SpirvStats;
namespace {
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 libspirv::CapabilityToString(static_cast<SpvCapability>(word));
}
std::string KeyIsLabel(std::string key) {
return key;
}
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);
}
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) {
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);
}

View File

@ -0,0 +1,45 @@
// 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.
#ifndef LIBSPIRV_TOOLS_STATS_STATS_ANALYZER_H_
#define LIBSPIRV_TOOLS_STATS_STATS_ANALYZER_H_
#include <unordered_map>
#include "source/spirv_stats.h"
class StatsAnalyzer {
public:
explicit StatsAnalyzer(const libspirv::SpirvStats& stats);
void WriteVersion(std::ostream& out);
void WriteGenerator(std::ostream& out);
void WriteCapability(std::ostream& out);
void WriteExtension(std::ostream& out);
void WriteOpcode(std::ostream& out);
private:
const libspirv::SpirvStats& stats_;
uint32_t num_modules_;
std::unordered_map<uint32_t, double> version_freq_;
std::unordered_map<uint32_t, double> generator_freq_;
std::unordered_map<uint32_t, double> capability_freq_;
std::unordered_map<std::string, double> extension_freq_;
std::unordered_map<uint32_t, double> opcode_freq_;
};
#endif // LIBSPIRV_TOOLS_STATS_STATS_ANALYZER_H_