/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ #include #include #include #include #include #include #include "bench/BenchUtils.h" #include "fbgemm/Fbgemm.h" #include "fbgemm/FbgemmSparse.h" #include "fbgemm/spmmUtils.h" using namespace std; using namespace fbgemm; vector qGranularityVals{ QuantizationGranularity::TENSOR, QuantizationGranularity::OUT_CHANNEL}; namespace { // tuple represents #rows, #cols, fuse_relu, quantization_granularity class RequantizeTest : public testing::TestWithParam< tuple> {}; }; // namespace INSTANTIATE_TEST_CASE_P( InstantiationName, RequantizeTest, ::testing::Combine( ::testing::ValuesIn( {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 20, 32}), // number of // rows ::testing::ValuesIn({1, 2, 3, 4, 16, 31, 32}), // number of columns ::testing::Bool(), // fuse relu ::testing::Bool(), // use bias ::testing::ValuesIn(qGranularityVals))); // requantization granularity TEST_P(RequantizeTest, reqTest) { int rows, cols; bool fuse_relu; bool use_bias; QuantizationGranularity q_gran; tie(rows, cols, fuse_relu, use_bias, q_gran) = GetParam(); int numElements = rows * cols; // Each row of weight matrix has it's own scale // The following is a multiplication activation scale with // weight scales. aligned_vector act_times_w_scale(rows); randFill(act_times_w_scale, -8.0f, 8.0f); random_device rd; mt19937 gen(rd()); auto distribution = uniform_int_distribution<>(0, 5); int32_t act_zero_point = distribution(gen); // Each row of weight matrix has it's own zero point aligned_vector weight_zero_point(rows); randFill(weight_zero_point, -8, 8); aligned_vector act_col_offsets(cols); // We are randomly filling act col offsets for the // purpose of this test. In reality, these will be calculated // by summing the columns of activations. randFill(act_col_offsets, -8, 8); aligned_vector weight_row_offsets(rows); randFill(weight_row_offsets, -8, 8); aligned_vector bias(rows); randFill(bias, -8.0f, 8.0f); aligned_vector input(numElements); randFill(input, -8, 8); aligned_vector output_ref(numElements); aligned_vector output_test(numElements); // output scale and zero point float scale = 2.0f; int32_t zero_point = 2; block_type_t block{0, rows, 0, cols}; auto bias_data_ptr = use_bias ? bias.data() : nullptr; bool use_col_offsets = true; if (q_gran == QuantizationGranularity::TENSOR) { if (weight_zero_point[0] == 0) { use_col_offsets = false; } } else { auto areEqual = [](int a, int b) { return a == b; }; if (std::all_of( weight_zero_point.begin(), weight_zero_point.end(), std::bind(areEqual, std::placeholders::_1, 0))) { use_col_offsets = false; } } auto col_offsets_ptr = use_col_offsets ? act_col_offsets.data() : nullptr; trRequantizationParams_t reqParams = { act_zero_point, weight_zero_point.data(), zero_point, scale, weight_row_offsets.data(), col_offsets_ptr, bias_data_ptr, act_times_w_scale.data()}; #define TESTCODE(FUSE_RELU, ACT_SYMMETRIC, WEIGHT_SYMMETRIC, HAS_BIAS, Q_GRAN) \ trRequantizeRef( \ output_ref.data(), input.data(), block, cols, cols, reqParams); \ trRequantizeOpt< \ FUSE_RELU, \ ACT_SYMMETRIC, \ WEIGHT_SYMMETRIC, \ HAS_BIAS, \ Q_GRAN>(output_test.data(), input.data(), block, cols, cols, reqParams); if (fuse_relu) { if (q_gran == QuantizationGranularity::TENSOR) { // Assume weight matrix has the same scale and the same // zero point for all rows. // Only weight_zero_point[0] and act_times_w_scale[0] is used // in calculations if (weight_zero_point[0] == 0 || !use_col_offsets) { if (act_zero_point == 0) { if (use_bias) { TESTCODE(true, true, true, true, QuantizationGranularity::TENSOR) } else { TESTCODE(true, true, true, false, QuantizationGranularity::TENSOR) } } else { if (use_bias) { TESTCODE(true, false, true, true, QuantizationGranularity::TENSOR) } else { TESTCODE(true, false, true, false, QuantizationGranularity::TENSOR) } } } else { if (act_zero_point == 0) { if (use_bias) { TESTCODE(true, true, false, true, QuantizationGranularity::TENSOR) } else { TESTCODE(true, true, false, false, QuantizationGranularity::TENSOR) } } else { if (use_bias) { TESTCODE(true, false, false, true, QuantizationGranularity::TENSOR) } else { TESTCODE(true, false, false, false, QuantizationGranularity::TENSOR) } } } } else if (q_gran == QuantizationGranularity::OUT_CHANNEL) { if (!use_col_offsets) { if (act_zero_point == 0) { if (use_bias) { TESTCODE( true, true, true, true, QuantizationGranularity::OUT_CHANNEL) } else { TESTCODE( true, true, true, false, QuantizationGranularity::OUT_CHANNEL) } } else { if (use_bias) { TESTCODE( true, false, true, true, QuantizationGranularity::OUT_CHANNEL) } else { TESTCODE( true, false, true, false, QuantizationGranularity::OUT_CHANNEL) } } } else { if (act_zero_point == 0) { if (use_bias) { TESTCODE( true, true, false, true, QuantizationGranularity::OUT_CHANNEL) } else { TESTCODE( true, true, false, false, QuantizationGranularity::OUT_CHANNEL) } } else { if (use_bias) { TESTCODE( true, false, false, true, QuantizationGranularity::OUT_CHANNEL) } else { TESTCODE( true, false, false, false, QuantizationGranularity::OUT_CHANNEL) } } } } else { FAIL(); } } else { if (q_gran == QuantizationGranularity::TENSOR) { if (weight_zero_point[0] == 0 || !use_col_offsets) { if (act_zero_point == 0) { if (use_bias) { TESTCODE(false, true, true, true, QuantizationGranularity::TENSOR) } else { TESTCODE(false, true, true, false, QuantizationGranularity::TENSOR) } } else { if (use_bias) { TESTCODE(false, false, true, true, QuantizationGranularity::TENSOR) } else { TESTCODE(false, false, true, false, QuantizationGranularity::TENSOR) } } } else { if (act_zero_point == 0) { if (use_bias) { TESTCODE(false, true, false, true, QuantizationGranularity::TENSOR) } else { TESTCODE(false, true, false, false, QuantizationGranularity::TENSOR) } } else { if (use_bias) { TESTCODE(false, false, false, true, QuantizationGranularity::TENSOR) } else { TESTCODE( false, false, false, false, QuantizationGranularity::TENSOR) } } } } else if (q_gran == QuantizationGranularity::OUT_CHANNEL) { if (!use_col_offsets) { if (act_zero_point == 0) { if (use_bias) { TESTCODE( false, true, true, true, QuantizationGranularity::OUT_CHANNEL) } else { TESTCODE( false, true, true, false, QuantizationGranularity::OUT_CHANNEL) } } else { if (use_bias) { TESTCODE( false, false, true, true, QuantizationGranularity::OUT_CHANNEL) } else { TESTCODE( false, false, true, false, QuantizationGranularity::OUT_CHANNEL) } } } else { if (act_zero_point == 0) { if (use_bias) { TESTCODE( false, true, false, true, QuantizationGranularity::OUT_CHANNEL) } else { TESTCODE( false, true, false, false, QuantizationGranularity::OUT_CHANNEL) } } else { if (use_bias) { TESTCODE( false, false, false, true, QuantizationGranularity::OUT_CHANNEL) } else { TESTCODE( false, false, false, false, QuantizationGranularity::OUT_CHANNEL) } } } } else { FAIL(); } } #undef TESTCODE ASSERT_EQ(output_ref, output_test) << "reference doesn't match with test"; }