mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-24 00:40:14 +00:00
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:
parent
72debb8fd4
commit
4f216402ba
@ -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
113
source/spirv_stats.cpp
Normal 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
50
source/spirv_stats.h
Normal 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_
|
@ -161,3 +161,4 @@ add_spvtools_unittest(
|
||||
|
||||
add_subdirectory(opt)
|
||||
add_subdirectory(val)
|
||||
add_subdirectory(stats)
|
||||
|
31
test/stats/CMakeLists.txt
Normal file
31
test/stats/CMakeLists.txt
Normal 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}
|
||||
)
|
238
test/stats/stats_aggregate_test.cpp
Normal file
238
test/stats/stats_aggregate_test.cpp
Normal 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
|
143
test/stats/stats_analyzer_test.cpp
Normal file
143
test/stats/stats_analyzer_test.cpp
Normal 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
|
@ -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
152
tools/stats/stats.cpp
Normal 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;
|
||||
}
|
127
tools/stats/stats_analyzer.cpp
Normal file
127
tools/stats/stats_analyzer.cpp
Normal 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);
|
||||
}
|
45
tools/stats/stats_analyzer.h
Normal file
45
tools/stats/stats_analyzer.h
Normal 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_
|
Loading…
Reference in New Issue
Block a user