Skip to content

Commit

Permalink
Vladislav comments apply
Browse files Browse the repository at this point in the history
  • Loading branch information
chenhu-wang committed May 29, 2024
1 parent ebc247a commit 19a4833
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
# Snippets LIR passes serialization

LIR(Linear Intermediate Representation) is used as the graph format in control flow pipeline, where dozens of passes are applied to LIR. This is to transfer the graph gradually to the stage that can generate kernel directly via expression emit. When each pass applied to LIR, there are some changes to the LIR. Developers maybe want check if the the result is as expected. This capability is introduced to serialize LIRs after every pass, then developer can check all these LIR stages.
LIR(Linear Intermediate Representation) is used as graph reprsentation in control flow pipeline, where dozens of passes are applied to LIR. This is to transfer the graph gradually to the stage that can generate kernel directly via expression instruction emission. When each pass is applied to LIR, there are some expected changes. Developers maybe want to check if the the result is as expected. This capability is introduced to serialize LIRs before and after passes, then developer can check these LIR changes and stages.

To turn on snippets LIR passses serialization feature, the following environment variable should be used to set the folder that the LIRs is dumped to:
To turn on snippets LIR passses serialization feature, the following environment variable should be used:
```sh
OV_CPU_SNIPPETS_LIR_PATH=<folder_path> binary ...
OV_SNIPPETS_DUMP_LIR=<space_separated_options> binary ...
```

Examples:
```sh
OV_CPU_SNIPPETS_LIR_PATH="/home/debug/LIRs" binary ...
OV_CPU_SNIPPETS_LIR_PATH="/home/debug/LIRs/" binary ...
```
OV_SNIPPETS_DUMP_LIR="passes=all dir=path/dumpdir formats=all" binary ...
OV_SNIPPETS_DUMP_LIR="passes=Insert dir=path/dumpdir formats=control_flow" binary ...
OV_SNIPPETS_DUMP_LIR="passes=Insert formats=data_flow" binary ...
```

Option names are case insensitive, the following options are supported:
- `passes` : Dump LIR around the pass if the set value is included in pass name. Key word 'all' means to dump LIR around every pass. If not set, will not dump.
- `dir` : Path to dumped LIR files. If omitted, it defaults to intel_snippets_LIR_dump.
- `formats` : Support value of control_flow, data_flow or all. If omitted, it defaults to control_flow.
181 changes: 181 additions & 0 deletions src/common/snippets/include/snippets/debug_caps.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#ifdef SNIPPETS_DEBUG_CAPS

#pragma once

#include "openvino/util/common_util.hpp"
#include "openvino/core/except.hpp"

#include <bitset>
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>

namespace ov {
namespace snippets {

class DebugCapsConfig {
private:
struct PropertySetter;
using PropertySetterPtr = std::shared_ptr<PropertySetter>;

public:
DebugCapsConfig() {
readProperties();
}

struct LIRFormatFilter {
enum Type : uint8_t {
controlFlow = 0, dataFlow, NumOfTypes
};
std::bitset<NumOfTypes> filter;

PropertySetterPtr getPropertySetter() {
return PropertySetterPtr(new BitsetFilterPropertySetter<NumOfTypes>("formats", filter, {{"all", {controlFlow, dataFlow}},
{"control_flow", {controlFlow}},
{"data_flow", {dataFlow}},
}));
}
};

struct PropertyGroup {
virtual std::vector<PropertySetterPtr> getPropertySetters() = 0;

void parseAndSet(const std::string& str) {
const auto& options = ov::util::split(str, ' ');
const auto& propertySetters = getPropertySetters();
bool failed = false;
auto getHelp = [propertySetters]() {
std::string help;
for (const auto& property : propertySetters)
help.append('\t' + property->getPropertyName() + "=<" + property->getPropertyValueDescription() + ">\n");
return help;
};

for (const auto& option : options) {
const auto& parts = ov::util::split(option, '=');
if (parts.size() > 2) {
failed = true;
break;
}
const auto& propertyName = ov::util::to_lower(parts.front());
const auto& foundSetter = std::find_if(propertySetters.begin(), propertySetters.end(),
[propertyName] (const PropertySetterPtr& setter) { return setter->getPropertyName() == propertyName; });
if (foundSetter == propertySetters.end() ||
!(*foundSetter)->parseAndSet(parts.size() == 1 ? "" : parts.back())) {
failed = true;
break;
}
}

if (failed)
OPENVINO_THROW(
"Wrong syntax: ",
str,
"\n",
"The following space separated options are supported (option names are case insensitive):",
"\n",
getHelp());
}
};

struct : PropertyGroup {
std::string dir = "intel_snippets_LIR_dump";
LIRFormatFilter format = { 1 << LIRFormatFilter::controlFlow };
std::string passes = "";

std::vector<PropertySetterPtr> getPropertySetters() override {
return { PropertySetterPtr(new StringPropertySetter("dir", dir, "path to dumped LIRs")),
format.getPropertySetter(),
PropertySetterPtr(new StringPropertySetter("passes", passes, "indicate dumped LIRs around these passes"))};
}
} dumpLIR;

private:
struct PropertySetter {
virtual bool parseAndSet(const std::string& str) = 0;
virtual std::string getPropertyValueDescription() const = 0;

PropertySetter(std::string name) : propertyName(std::move(name)) {}

virtual ~PropertySetter() = default;

const std::string& getPropertyName() const { return propertyName; }

private:
const std::string propertyName;
};

struct StringPropertySetter : PropertySetter {
StringPropertySetter(const std::string& name, std::string& ref, const std::string&& valueDescription)
: PropertySetter(name), property(ref), propertyValueDescription(valueDescription) {}

~StringPropertySetter() override = default;

bool parseAndSet(const std::string& str) override {
property = str;
return true;
}
std::string getPropertyValueDescription() const override { return propertyValueDescription; }

private:
std::string& property;
const std::string propertyValueDescription;
};

template<std::size_t NumOfBits>
struct BitsetFilterPropertySetter : PropertySetter {
struct Token {
std::string name;
std::vector<size_t> bits;
};

BitsetFilterPropertySetter(const std::string& name, std::bitset<NumOfBits>& ref, const std::vector<Token>&& tokens)
: PropertySetter(name), property(ref), propertyTokens(tokens) {}

~BitsetFilterPropertySetter() override = default;

bool parseAndSet(const std::string& str) override {
const auto& tokens = str.empty() ?
std::vector<std::string>{"all"} : ov::util::split(ov::util::to_lower(str), ',');
property.reset();
for (const auto& token : tokens) {
const bool tokenVal = (token.front() != '-');
const auto& tokenName = tokenVal ? token : token.substr(1);
const auto& foundToken = std::find_if(propertyTokens.begin(), propertyTokens.end(),
[tokenName] (const Token& token) { return token.name == tokenName; });
if (foundToken == propertyTokens.end())
return false;

for (const auto& bit : foundToken->bits) {
property.set(bit, tokenVal);
}
}
return true;
}
std::string getPropertyValueDescription() const override {
std::string supportedTokens = "comma separated filter tokens: ";
for (size_t i = 0; i < propertyTokens.size(); i++) {
if (i)
supportedTokens.push_back(',');
supportedTokens.append(propertyTokens[i].name);
}
supportedTokens.append("; -'token' is used for exclusion, case does not matter, no tokens is treated as 'all'");
return supportedTokens;
}

private:
std::bitset<NumOfBits>& property;
const std::vector<Token> propertyTokens;
};

void readProperties();
};

} // namespace snippets
} // namespace ov

#endif // SNIPPETS_DEBUG_CAPS
5 changes: 5 additions & 0 deletions src/common/snippets/include/snippets/lowered/linear_ir.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
#include "snippets/target_machine.hpp"
#include "snippets/shape_inference/shape_inference.hpp"

#ifdef SNIPPETS_DEBUG_CAPS
#include "snippets/debug_caps.hpp"
#endif

namespace ov {
namespace snippets {
namespace lowered {
Expand All @@ -35,6 +39,7 @@ class Config {
size_t m_loop_depth = 1;
#ifdef SNIPPETS_DEBUG_CAPS
PerfCountMode perf_count_mode = PerfCountMode::Disabled;
DebugCapsConfig debug_config;
#endif
// Some Subgraphs doesn't support domain optimization due to operations' semantics
bool m_enable_domain_optimization = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ class PassConfig {
friend bool operator==(const PassConfig& lhs, const PassConfig& rhs);
friend bool operator!=(const PassConfig& lhs, const PassConfig& rhs);

#ifdef SNIPPETS_DEBUG_CAPS
std::string LIRPath;
#endif

private:
std::unordered_set<DiscreteTypeInfo> m_disabled;
std::unordered_set<DiscreteTypeInfo> m_enabled;
Expand Down
28 changes: 28 additions & 0 deletions src/common/snippets/src/debug_caps.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#ifdef SNIPPETS_DEBUG_CAPS

#include "snippets/debug_caps.hpp"

namespace ov {
namespace snippets {

void DebugCapsConfig::readProperties() {
auto readEnv = [](const char* envVar) {
const char* env = std::getenv(envVar);
if (env && *env)
return env;

return (const char*)nullptr;
};

const char* envVarValue = nullptr;
if ((envVarValue = readEnv("OV_SNIPPETS_DUMP_LIR")))
dumpLIR.parseAndSet(envVarValue);
}

} // namespace snippets
} // namespace ov

#endif // SNIPPETS_DEBUG_CAPS
45 changes: 38 additions & 7 deletions src/common/snippets/src/lowered/pass/pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#ifdef SNIPPETS_DEBUG_CAPS
#include "snippets/lowered/pass/serialize_control_flow.hpp"
#include "snippets/lowered/pass/serialize_data_flow.hpp"
#endif

#include "snippets/utils.hpp"
Expand Down Expand Up @@ -36,11 +37,34 @@ void PassPipeline::run(LinearIR& linear_ir) const {

void PassPipeline::run(LinearIR& linear_ir, LinearIR::constExprIt begin, LinearIR::constExprIt end) const {
#ifdef SNIPPETS_DEBUG_CAPS
auto path = m_pass_config->LIRPath;
if (!path.empty() && path.compare(path.size() - 1, 1, "/")) // user can set folder path to ".../path" or ".../path/"
path = path + "/";
const auto& dumpLIR = linear_ir.get_config().debug_config.dumpLIR;
bool enable_dump = false;
std::string pass_key;
std::string directory;
if (!dumpLIR.passes.empty()) {
enable_dump = true;
pass_key = dumpLIR.passes;
directory = dumpLIR.dir + "/";
}
#endif
for (const auto& pass : m_passes) {
#ifdef SNIPPETS_DEBUG_CAPS
bool actived_pass = false;
auto pass_name = std::string(pass->get_type_name());
if (enable_dump && (pass_key == "all" || (pass_name.find(pass_key) != std::string::npos))) {
actived_pass = true;
if (dumpLIR.format.filter[DebugCapsConfig::LIRFormatFilter::controlFlow]) {
std::string xml_path = directory + pass_name + "_control_flow_in.xml";
lowered::pass::SerializeControlFlow SerializeLIR(xml_path);
SerializeLIR.run(linear_ir);
}
if (dumpLIR.format.filter[DebugCapsConfig::LIRFormatFilter::dataFlow]) {
std::string xml_path = directory + pass_name + "_data_flow_in.xml";
lowered::pass::SerializeDataFlow SerializeLIR(xml_path);
SerializeLIR.run(linear_ir);
}
}
#endif
OPENVINO_ASSERT(pass != nullptr, "PassPipeline has empty pass!");
if (m_pass_config->is_disabled(pass->get_type_info())) {
continue;
Expand All @@ -53,10 +77,17 @@ void PassPipeline::run(LinearIR& linear_ir, LinearIR::constExprIt begin, LinearI
OPENVINO_THROW("Unexpected pass (", pass->get_type_info(), ") is registered in PassPipeline");
}
#ifdef SNIPPETS_DEBUG_CAPS
if (!path.empty()) {
std::string xml_path = path + std::string(pass->get_type_name()) + ".xml";
lowered::pass::SerializeControlFlow SerializeLIR(xml_path);
SerializeLIR.run(linear_ir);
if (actived_pass) {
if (dumpLIR.format.filter[DebugCapsConfig::LIRFormatFilter::controlFlow]) {
std::string xml_path = directory + pass_name + "_control_flow_out.xml";
lowered::pass::SerializeControlFlow SerializeLIR(xml_path);
SerializeLIR.run(linear_ir);
}
if (dumpLIR.format.filter[DebugCapsConfig::LIRFormatFilter::dataFlow]) {
std::string xml_path = directory + pass_name + "_data_flow_out.xml";
lowered::pass::SerializeDataFlow SerializeLIR(xml_path);
SerializeLIR.run(linear_ir);
}
}
#endif
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2023-2024 Intel Corporation
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#ifdef SNIPPETS_DEBUG_CAPS
Expand All @@ -18,9 +18,6 @@ void SnippetsDebugCapsConfig::readProperties() {
};

enable_segfault_detector = readEnv("OV_CPU_SNIPPETS_SEGFAULT_DETECTOR") ? true : false;
const char* envVarValue = nullptr;
if ((envVarValue = readEnv("OV_CPU_SNIPPETS_LIR_PATH")))
LIRSerializePath = envVarValue;
}

} // namespace intel_cpu
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2023-2024 Intel Corporation
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#ifdef SNIPPETS_DEBUG_CAPS
Expand All @@ -18,7 +18,6 @@ class SnippetsDebugCapsConfig {
}

bool enable_segfault_detector;
std::string LIRSerializePath;

private:
void readProperties();
Expand Down
4 changes: 0 additions & 4 deletions src/plugins/intel_cpu/src/nodes/subgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -923,10 +923,6 @@ void Subgraph::SubgraphExecutor::parallel_forNd(const std::function<void(jit_sni
});
}

#ifdef SNIPPETS_DEBUG_CAPS
const auto target = std::dynamic_pointer_cast<const CPUTargetMachine>(snippetAttrs.snippet->get_generator()->get_target_machine());
lowering_config->LIRPath = target->debug_config.LIRSerializePath;
#endif
} // namespace node
} // namespace intel_cpu
} // namespace ov

0 comments on commit 19a4833

Please sign in to comment.