[Operator Versioning][Edge] Reorganize upgrader initialization logic for thread safety (#70225)

Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/70225

Thanks for zhxchen17's suggestion. This pr move the operator initialization logic to `upgrader_mobile.cpp`, such that we can leverage the static variable to ensure the operator initialization only happens once.
ghstack-source-id: 146103229

Test Plan:
```

buck test mode/opt //papaya/integration/service/test/analytics/histogram:generic_histogram_system_test -- --exact 'papaya/integration/service/test/analytics/histogram:generic_histogram_system_test - SumHistogramSystemTest.test' --run-disabled
buck test mode/opt //caffe2/test/cpp/jit:jit
buck test mode/dev //papaya/integration/service/test/mnist:mnist_system_test -- --exact 'papaya/integration/service/test/mnist:mnist_system_test - MnistFederatedSystemTest.test'
```

Reviewed By: zhxchen17

Differential Revision: D33247543

fbshipit-source-id: 6c3a87fe909a1be01452fa79649065845b26d805
This commit is contained in:
Chen Lai
2021-12-21 17:24:52 -08:00
committed by Facebook GitHub Bot
parent 21c6de9fdc
commit 591ca4d6bc
3 changed files with 244 additions and 255 deletions

View File

@ -226,7 +226,6 @@ class BytecodeDeserializer final {
// operators from the given model. If it's less than the current runtime,
// upgrader will be applied at loading stage.
uint64_t operator_version_;
std::atomic<bool> _upgrader_initialized{false};
};
BytecodeDeserializer::BytecodeDeserializer(
@ -302,34 +301,8 @@ void BytecodeDeserializer::parseFunctionSchema(
}
void BytecodeDeserializer::init_upgrader(mobile::Function* function) {
// Upgrader should only be initialized once when runtime loads the first
// module. It no longer needs to initialized afterwards. Previously, instead
// of using an atomic variable, the upgrader will be initailized depends on
// whether byteCodeFunctionWithOperator.function.get_code().operators_ is
// empty. If it's empty, it means the operator from the upgrader is not
// initialized yet. However, it's not thread safe. When multiple thread loads
// module together, it's possible that they all consider it's the first
// module. Use an atomic variable here to make sure it's thread safe.
if (!_upgrader_initialized.load(std::memory_order_seq_cst)) {
for (auto& byteCodeFunctionWithOperator : getUpgraderBytecodeList()) {
// When kUpgraderByteCode is initialized in upgrader_mobile.h, the mobile
// function is initialized with everything (instruction, constants, types,
// registerer size and etc), except operator. The operator function is
// also static initialized and is available later. The oprator for the
// upgrader function will be initialized when the first module is loaded.
for (const auto& op : byteCodeFunctionWithOperator.operators) {
byteCodeFunctionWithOperator.function.append_operator(
op.name,
op.overload_name,
op.num_specified_args,
caffe2::serialize::kMaxSupportedFileFormatVersion);
}
// Add the upgrader function in code.
function->append_function(byteCodeFunctionWithOperator.function);
}
// Set the flag _upgrader_initialized to ture, and for the 2, 3, ...
// modules, no need to initalized again
_upgrader_initialized.store(true, std::memory_order_seq_cst);
for (auto& byteCodeFunctionWithOperator : getUpgraderBytecodeList()) {
function->append_function(byteCodeFunctionWithOperator.function);
}
}

View File

@ -1,3 +1,4 @@
#include <caffe2/serialize/versions.h>
#include <torch/csrc/jit/mobile/upgrader_mobile.h>
namespace c10 {
@ -25,231 +26,245 @@ getOperatorVersionMapForMobile() {
return operatorVersionMapForMobile;
}
std::vector<ByteCodeFunctionWithOperator>& getUpgraderBytecodeList() {
static std::vector<ByteCodeFunctionWithOperator> upgraderBytecodeList({
ByteCodeFunctionWithOperator({
mobile::Function::registerFunc(
"div_Tensor_0_3",
std::vector<Instruction>({
Instruction{OpCode::STOREN, 1, 2},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::STORE, 3, 0},
Instruction{OpCode::MOVE, 3, 0},
Instruction{OpCode::JF, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 1, 0},
Instruction{OpCode::JMP, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::LOADC, 0, 0},
Instruction{OpCode::OP, 2, 0},
Instruction{OpCode::STORE, 4, 0},
Instruction{OpCode::DROPR, 2, 0},
Instruction{OpCode::DROPR, 1, 0},
Instruction{OpCode::MOVE, 4, 0},
Instruction{OpCode::RET, 0, 0},
}), // instructions_
std::vector<c10::IValue>({
c10::IValue("trunc"),
c10::IValue(true),
}), // constants
std::vector<c10::TypePtr>(), // types
4 // register_size_
),
std::vector<OperatorString>({
OperatorString({"aten::is_floating_point", "", 1}),
OperatorString({"aten::div", "Tensor", 2}),
OperatorString({"aten::div", "Tensor_mode", 3}),
}), // op_names
}),
ByteCodeFunctionWithOperator({
mobile::Function::registerFunc(
"div_Scalar_0_3",
std::vector<Instruction>({
Instruction{OpCode::STOREN, 1, 2},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::ISINSTANCE, 0, 1},
Instruction{OpCode::STORE, 3, 0},
Instruction{OpCode::MOVE, 3, 0},
Instruction{OpCode::JF, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 1, 0},
Instruction{OpCode::JMP, 6, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 2, 0},
Instruction{OpCode::LOADC, 0, 0},
Instruction{OpCode::OP, 3, 0},
Instruction{OpCode::STORE, 4, 0},
Instruction{OpCode::DROPR, 2, 0},
Instruction{OpCode::DROPR, 1, 0},
Instruction{OpCode::MOVE, 4, 0},
Instruction{OpCode::RET, 0, 0},
}), // instructions_
std::vector<c10::IValue>({
c10::IValue("trunc"),
c10::IValue(true),
}), // constants
std::vector<c10::TypePtr>({c10::parseType("float")}), // types
4 // register_size_
),
std::vector<OperatorString>({
OperatorString({"aten::is_floating_point", "", 1}),
OperatorString({"aten::div", "Scalar", 2}),
OperatorString({"prim::unchecked_cast", "", 1}),
OperatorString({"aten::div", "Scalar_mode", 3}),
}), // op_names
}),
ByteCodeFunctionWithOperator({
mobile::Function::registerFunc(
"div_out_0_3",
std::vector<Instruction>({
Instruction{OpCode::STOREN, 1, 3},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 3, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::STORE, 4, 0},
Instruction{OpCode::MOVE, 4, 0},
Instruction{OpCode::JF, 6, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::LOAD, 3, 0},
Instruction{OpCode::OP, 1, 0},
Instruction{OpCode::JMP, 6, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::LOADC, 0, 0},
Instruction{OpCode::LOAD, 3, 0},
Instruction{OpCode::OP, 2, 0},
Instruction{OpCode::STORE, 5, 0},
Instruction{OpCode::DROPR, 3, 0},
Instruction{OpCode::DROPR, 2, 0},
Instruction{OpCode::DROPR, 1, 0},
Instruction{OpCode::MOVE, 5, 0},
Instruction{OpCode::RET, 0, 0},
}), // instructions_
std::vector<c10::IValue>({
c10::IValue("trunc"),
c10::IValue(true),
}), // constants
std::vector<c10::TypePtr>(), // types
5 // register_size_
),
std::vector<OperatorString>({
OperatorString({"aten::is_floating_point", "", 1}),
OperatorString({"aten::div", "out", 3}),
OperatorString({"aten::div", "out_mode", 4}),
}), // op_names
}),
ByteCodeFunctionWithOperator({
mobile::Function::registerFunc(
"div__Tensor_0_3",
std::vector<Instruction>({
Instruction{OpCode::STOREN, 1, 2},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::STORE, 3, 0},
Instruction{OpCode::MOVE, 3, 0},
Instruction{OpCode::JF, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 1, 0},
Instruction{OpCode::JMP, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::LOADC, 0, 0},
Instruction{OpCode::OP, 2, 0},
Instruction{OpCode::STORE, 4, 0},
Instruction{OpCode::DROPR, 2, 0},
Instruction{OpCode::DROPR, 1, 0},
Instruction{OpCode::MOVE, 4, 0},
Instruction{OpCode::RET, 0, 0},
}), // instructions_
std::vector<c10::IValue>({
c10::IValue("trunc"),
c10::IValue(true),
}), // constants
std::vector<c10::TypePtr>(), // types
4 // register_size_
),
std::vector<OperatorString>({
OperatorString({"aten::is_floating_point", "", 1}),
OperatorString({"aten::div_", "Tensor", 2}),
OperatorString({"aten::div_", "Tensor_mode", 3}),
}), // op_names
}),
ByteCodeFunctionWithOperator({
mobile::Function::registerFunc(
"div__Scalar_0_3",
std::vector<Instruction>({
Instruction{OpCode::STOREN, 1, 2},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::ISINSTANCE, 0, 1},
Instruction{OpCode::STORE, 3, 0},
Instruction{OpCode::MOVE, 3, 0},
Instruction{OpCode::JF, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 1, 0},
Instruction{OpCode::JMP, 6, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 2, 0},
Instruction{OpCode::LOADC, 0, 0},
Instruction{OpCode::OP, 3, 0},
Instruction{OpCode::STORE, 4, 0},
Instruction{OpCode::DROPR, 2, 0},
Instruction{OpCode::DROPR, 1, 0},
Instruction{OpCode::MOVE, 4, 0},
Instruction{OpCode::RET, 0, 0},
}), // instructions_
std::vector<c10::IValue>({
c10::IValue("trunc"),
c10::IValue(true),
}), // constants
std::vector<c10::TypePtr>(), // types
4 // register_size_
),
std::vector<OperatorString>({
OperatorString({"aten::is_floating_point", "", 1}),
OperatorString({"aten::div_", "Scalar", 2}),
OperatorString({"prim::unchecked_cast", "", 1}),
OperatorString({"aten::div_", "Scalar_mode", 3}),
}), // op_names
}),
});
const std::vector<ByteCodeFunctionWithOperator>& getUpgraderBytecodeList() {
auto generate_upgrader_bytecode_list = []() {
std::vector<ByteCodeFunctionWithOperator> upgrader_function_list({
ByteCodeFunctionWithOperator({
mobile::Function::registerFunc(
"div_Tensor_0_3",
std::vector<Instruction>({
Instruction{OpCode::STOREN, 1, 2},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::STORE, 3, 0},
Instruction{OpCode::MOVE, 3, 0},
Instruction{OpCode::JF, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 1, 0},
Instruction{OpCode::JMP, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::LOADC, 0, 0},
Instruction{OpCode::OP, 2, 0},
Instruction{OpCode::STORE, 4, 0},
Instruction{OpCode::DROPR, 2, 0},
Instruction{OpCode::DROPR, 1, 0},
Instruction{OpCode::MOVE, 4, 0},
Instruction{OpCode::RET, 0, 0},
}), // instructions_
std::vector<c10::IValue>({
c10::IValue("trunc"),
c10::IValue(true),
}), // constants
std::vector<c10::TypePtr>(), // types
4 // register_size_
),
std::vector<OperatorString>({
OperatorString({"aten::is_floating_point", "", 1}),
OperatorString({"aten::div", "Tensor", 2}),
OperatorString({"aten::div", "Tensor_mode", 3}),
}), // op_names
}),
ByteCodeFunctionWithOperator({
mobile::Function::registerFunc(
"div_Scalar_0_3",
std::vector<Instruction>({
Instruction{OpCode::STOREN, 1, 2},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::ISINSTANCE, 0, 1},
Instruction{OpCode::STORE, 3, 0},
Instruction{OpCode::MOVE, 3, 0},
Instruction{OpCode::JF, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 1, 0},
Instruction{OpCode::JMP, 6, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 2, 0},
Instruction{OpCode::LOADC, 0, 0},
Instruction{OpCode::OP, 3, 0},
Instruction{OpCode::STORE, 4, 0},
Instruction{OpCode::DROPR, 2, 0},
Instruction{OpCode::DROPR, 1, 0},
Instruction{OpCode::MOVE, 4, 0},
Instruction{OpCode::RET, 0, 0},
}), // instructions_
std::vector<c10::IValue>({
c10::IValue("trunc"),
c10::IValue(true),
}), // constants
std::vector<c10::TypePtr>({c10::parseType("float")}), // types
4 // register_size_
),
std::vector<OperatorString>({
OperatorString({"aten::is_floating_point", "", 1}),
OperatorString({"aten::div", "Scalar", 2}),
OperatorString({"prim::unchecked_cast", "", 1}),
OperatorString({"aten::div", "Scalar_mode", 3}),
}), // op_names
}),
ByteCodeFunctionWithOperator({
mobile::Function::registerFunc(
"div_out_0_3",
std::vector<Instruction>({
Instruction{OpCode::STOREN, 1, 3},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 3, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::STORE, 4, 0},
Instruction{OpCode::MOVE, 4, 0},
Instruction{OpCode::JF, 6, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::LOAD, 3, 0},
Instruction{OpCode::OP, 1, 0},
Instruction{OpCode::JMP, 6, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::LOADC, 0, 0},
Instruction{OpCode::LOAD, 3, 0},
Instruction{OpCode::OP, 2, 0},
Instruction{OpCode::STORE, 5, 0},
Instruction{OpCode::DROPR, 3, 0},
Instruction{OpCode::DROPR, 2, 0},
Instruction{OpCode::DROPR, 1, 0},
Instruction{OpCode::MOVE, 5, 0},
Instruction{OpCode::RET, 0, 0},
}), // instructions_
std::vector<c10::IValue>({
c10::IValue("trunc"),
c10::IValue(true),
}), // constants
std::vector<c10::TypePtr>(), // types
5 // register_size_
),
std::vector<OperatorString>({
OperatorString({"aten::is_floating_point", "", 1}),
OperatorString({"aten::div", "out", 3}),
OperatorString({"aten::div", "out_mode", 4}),
}), // op_names
}),
ByteCodeFunctionWithOperator({
mobile::Function::registerFunc(
"div__Tensor_0_3",
std::vector<Instruction>({
Instruction{OpCode::STOREN, 1, 2},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::STORE, 3, 0},
Instruction{OpCode::MOVE, 3, 0},
Instruction{OpCode::JF, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 1, 0},
Instruction{OpCode::JMP, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::LOADC, 0, 0},
Instruction{OpCode::OP, 2, 0},
Instruction{OpCode::STORE, 4, 0},
Instruction{OpCode::DROPR, 2, 0},
Instruction{OpCode::DROPR, 1, 0},
Instruction{OpCode::MOVE, 4, 0},
Instruction{OpCode::RET, 0, 0},
}), // instructions_
std::vector<c10::IValue>({
c10::IValue("trunc"),
c10::IValue(true),
}), // constants
std::vector<c10::TypePtr>(), // types
4 // register_size_
),
std::vector<OperatorString>({
OperatorString({"aten::is_floating_point", "", 1}),
OperatorString({"aten::div_", "Tensor", 2}),
OperatorString({"aten::div_", "Tensor_mode", 3}),
}), // op_names
}),
ByteCodeFunctionWithOperator({
mobile::Function::registerFunc(
"div__Scalar_0_3",
std::vector<Instruction>({
Instruction{OpCode::STOREN, 1, 2},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::OP, 0, 0},
Instruction{OpCode::JF, 3, 0},
Instruction{OpCode::LOADC, 1, 0},
Instruction{OpCode::JMP, 3, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::ISINSTANCE, 0, 1},
Instruction{OpCode::STORE, 3, 0},
Instruction{OpCode::MOVE, 3, 0},
Instruction{OpCode::JF, 5, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 1, 0},
Instruction{OpCode::JMP, 6, 0},
Instruction{OpCode::LOAD, 1, 0},
Instruction{OpCode::LOAD, 2, 0},
Instruction{OpCode::OP, 2, 0},
Instruction{OpCode::LOADC, 0, 0},
Instruction{OpCode::OP, 3, 0},
Instruction{OpCode::STORE, 4, 0},
Instruction{OpCode::DROPR, 2, 0},
Instruction{OpCode::DROPR, 1, 0},
Instruction{OpCode::MOVE, 4, 0},
Instruction{OpCode::RET, 0, 0},
}), // instructions_
std::vector<c10::IValue>({
c10::IValue("trunc"),
c10::IValue(true),
}), // constants
std::vector<c10::TypePtr>(), // types
4 // register_size_
),
std::vector<OperatorString>({
OperatorString({"aten::is_floating_point", "", 1}),
OperatorString({"aten::div_", "Scalar", 2}),
OperatorString({"prim::unchecked_cast", "", 1}),
OperatorString({"aten::div_", "Scalar_mode", 3}),
}), // op_names
}),
});
for (const auto& upgrader_function : upgrader_function_list) {
for (const auto& op : upgrader_function.operators) {
upgrader_function.function.append_operator(
op.name,
op.overload_name,
op.num_specified_args,
caffe2::serialize::kMaxSupportedFileFormatVersion);
}
}
return upgrader_function_list;
};
static std::vector<ByteCodeFunctionWithOperator> upgraderBytecodeList =
generate_upgrader_bytecode_list();
return upgraderBytecodeList;
}

View File

@ -36,7 +36,8 @@ struct ByteCodeFunctionWithOperator {
std::vector<OperatorString> operators;
};
TORCH_API std::vector<ByteCodeFunctionWithOperator>& getUpgraderBytecodeList();
TORCH_API const std::vector<ByteCodeFunctionWithOperator>&
getUpgraderBytecodeList();
} // namespace jit
} // namespace torch