Skip to content

Commit 65f558f

Browse files
jfix71facebook-github-bot
authored andcommitted
Fix quantization when doing parallel clone testing; add check for bitwise accuracy (#3440)
Summary: Previously we were only checking the results from parallel clones against the interpreter result within some threshold. With this change we will also check that all of the parallel clone results from the backend are bitwise accurate. When implementing this I found that things were currently broken; the quantizer was not erroring out when it should have been (fixed in 1st commit). Additionally we were not copying over the quantization infos for the cloned nodes to find their correct quantization parameters, so without the check from the 1st commit those nodes were just not being quantized. Pull Request resolved: #3440 Test Plan: All tests still pass with the added check; also ran OperatorTests with `--parallel-clone-count=10`. Differential Revision: D16944262 Pulled By: jfix71 fbshipit-source-id: 117abf463c409b1202fdbefd1139509c8d85165c
1 parent 4f27105 commit 65f558f

File tree

4 files changed

+52
-9
lines changed

4 files changed

+52
-9
lines changed

lib/Quantization/Quantization.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,11 @@ class FunctionQuantizer : public FunctionConverter {
294294
if (val.getElementType() == ElemKind::FloatTy) {
295295
needsQuantization = true;
296296
if (!quantizationParamsExist(val)) {
297+
CHECK(!assertAllNodesQuantized_)
298+
<< "Quantization parameters did not exist for an input NodeValue "
299+
"that should have been quantized; input number "
300+
<< idx << " of node:\n"
301+
<< node.getDebugDesc();
297302
return false;
298303
}
299304
}
@@ -305,6 +310,11 @@ class FunctionQuantizer : public FunctionConverter {
305310
if (val.getElementType() == ElemKind::FloatTy) {
306311
needsQuantization = true;
307312
if (!quantizationParamsExist(val)) {
313+
CHECK(!assertAllNodesQuantized_)
314+
<< "Quantization parameters did not exist for a result of a Node "
315+
"that should have been quantized; result number "
316+
<< idx << " of node:\n"
317+
<< node.getDebugDesc();
308318
return false;
309319
}
310320
}

tests/unittests/BackendTestUtils.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,6 @@ void compareAgainstInterpreter(llvm::StringRef backendName,
159159
FunctionTensorPair IFT = createAndInitFunction(iBindings, IEE);
160160
FunctionTensorPair BFT = createAndInitFunction(bBindings, BEE);
161161

162-
// Clone the Function inside itself many times if desired.
163-
std::unordered_set<Tensor *> resultTensors =
164-
cloneFunInsideFun(BFT, &bBindings, count);
165-
assert(resultTensors.size() == count &&
166-
"Should get the same number of Tensors back as count.");
167-
168162
Function *IF = IFT.first;
169163

170164
// Set up the configs for interpreter and backend. If one or both functions
@@ -178,6 +172,12 @@ void compareAgainstInterpreter(llvm::StringRef backendName,
178172
CompilationContext &cctxI = configs.first;
179173
CompilationContext &cctxB = configs.second;
180174

175+
// Clone the Function inside itself many times if desired.
176+
std::unordered_set<Tensor *> resultTensors =
177+
cloneFunInsideFun(BFT, &bBindings, cctxB, count);
178+
assert(resultTensors.size() == count &&
179+
"Should get the same number of Tensors back as count.");
180+
181181
BEE.compile(cctxB);
182182
BEE.run(bBindings);
183183

@@ -188,10 +188,19 @@ void compareAgainstInterpreter(llvm::StringRef backendName,
188188
for (Tensor *T : resultTensors) {
189189
EXPECT_TRUE(IFT.second->isEqual(*T, allowedError));
190190
}
191+
192+
// Additionally check that each of the results from the parallel cloned
193+
// Functions are bitwise equal.
194+
auto it = resultTensors.begin();
195+
Tensor *firstResult = *it;
196+
for (it++; it != resultTensors.end(); it++) {
197+
EXPECT_TRUE(firstResult->isBitwiseEqual(**it));
198+
}
191199
}
192200

193201
std::unordered_set<Tensor *> cloneFunInsideFun(FunctionTensorPair FTP,
194202
PlaceholderBindings *bindings,
203+
CompilationContext &cctx,
195204
unsigned count) {
196205
Function *origF = FTP.first;
197206

@@ -254,6 +263,26 @@ std::unordered_set<Tensor *> cloneFunInsideFun(FunctionTensorPair FTP,
254263
// Now erase the clone we used to copy in, as it's no longer needed.
255264
mod->eraseFunction(cloneF);
256265

266+
// Finally, duplicate all of the node quantization infos with the new expected
267+
// clone's name so that the cloned copies will find the same quantization info
268+
// as the original node if being quantized.
269+
auto &origInfos = cctx.precisionConfig.quantConfig.infos;
270+
origInfos.reserve(count * origInfos.size());
271+
std::vector<NodeQuantizationInfo> newInfos;
272+
newInfos.reserve((count - 1) * origInfos.size());
273+
for (const auto &QI : origInfos) {
274+
const size_t colonIdx = QI.nodeOutputName_.find(":");
275+
assert(colonIdx != std::string::npos && "Name should always contain ':'");
276+
for (size_t i = 1; i < count; i++) {
277+
std::string newName(QI.nodeOutputName_);
278+
// Cloned nodes end up with the original name plus the count number
279+
// appended to their name due to uniquing. Replicate the same thing.
280+
newName.insert(colonIdx, std::to_string(i));
281+
newInfos.emplace_back(newName, QI.tensorQuantizationParams_);
282+
}
283+
}
284+
origInfos.insert(origInfos.end(), newInfos.begin(), newInfos.end());
285+
257286
return resultTensors;
258287
}
259288

tests/unittests/BackendTestUtils.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,12 @@ void compareAgainstInterpreter(
239239
/// Given some \p FTP representing a Function with a single SaveNode and its
240240
/// Tensor output, duplicate the Nodes in the Function and their Placeholder
241241
/// inputs given \p bindings \p parallelCount times. \returns a set of Tensor
242-
/// pointers for each output of the cloned Function.
242+
/// pointers for each output of the cloned Function. If the quantization node
243+
/// info found in \p cctx exists, then all of the node infos will be cloned
244+
/// accordingly with the names of the newly cloned nodes added to the Function.
243245
std::unordered_set<Tensor *> cloneFunInsideFun(FunctionTensorPair FTP,
244246
PlaceholderBindings *bindings,
247+
CompilationContext &cctx,
245248
unsigned parallelCount);
246249

247250
void inferConvNet(Tensor *inputs, Tensor *filter, Tensor *bias, Tensor *out,

tests/unittests/OperatorTest.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -960,13 +960,14 @@ TEST_P(OperatorTest, matmul_ParCloneTest10) {
960960
auto *save = F_->createSave("save", R);
961961
auto *saveTensor = bindings_.allocate(save->getPlaceholder());
962962

963+
CompilationContext cctx;
963964
const unsigned parallelCount = 10;
964965
auto resultTensors = cloneFunInsideFun(std::make_pair(F_, saveTensor),
965-
&bindings_, parallelCount);
966+
&bindings_, cctx, parallelCount);
966967

967968
EXPECT_EQ(resultTensors.size(), parallelCount);
968969

969-
EE_.compile(CompilationMode::Infer);
970+
EE_.compile(cctx);
970971
EE_.run(bindings_);
971972

972973
for (Tensor *T : resultTensors) {

0 commit comments

Comments
 (0)