
There are several aspects of the API that either aren't easy to use, or are deceptively easy to do the wrong thing. The main change of this commit is to remove all of the `getValue<T>`/`getFlatValue<T>` from ElementsAttr and instead provide operator[] methods on the ranges returned by `getValues<T>`. This provides a much more convenient API for the value ranges. It also removes the easy-to-be-inefficient nature of getValue/getFlatValue, which under the hood would construct a new range for the type `T`. Constructing a range is not necessarily cheap in all cases, and could lead to very poor performance if used within a loop; i.e. if you were to naively write something like: ``` DenseElementsAttr attr = ...; for (int i = 0; i < size; ++i) { // We are internally rebuilding the APFloat value range on each iteration!! APFloat it = attr.getFlatValue<APFloat>(i); } ``` Differential Revision: https://reviews.llvm.org/D113229
173 lines
6.7 KiB
C++
173 lines
6.7 KiB
C++
//===- QuantizationUtilsTest.cpp - unit tests for quantization utils ------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "mlir/Dialect/Quant/QuantOps.h"
|
|
#include "mlir/Dialect/Quant/QuantizeUtils.h"
|
|
#include "mlir/Dialect/Quant/UniformSupport.h"
|
|
#include "mlir/IR/Attributes.h"
|
|
#include "mlir/IR/BuiltinTypes.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace mlir;
|
|
using namespace mlir::quant;
|
|
|
|
namespace {
|
|
|
|
// Test UniformQuantizedValueConverter converts all APFloat to a magic number 5.
|
|
class TestUniformQuantizedValueConverter
|
|
: public UniformQuantizedValueConverter {
|
|
public:
|
|
TestUniformQuantizedValueConverter(UniformQuantizedType type)
|
|
: UniformQuantizedValueConverter(type), qtype(type) {}
|
|
APInt quantizeFloatToInt(APFloat expressedValue) const {
|
|
return APInt(qtype.getStorageType().cast<IntegerType>().getWidth(), 5L);
|
|
}
|
|
|
|
private:
|
|
UniformQuantizedType qtype;
|
|
};
|
|
|
|
Attribute getTestFloatAttr(double value, MLIRContext *ctx) {
|
|
return FloatAttr::get(FloatType::getF32(ctx), value);
|
|
}
|
|
|
|
template <typename ConcreteAttrClass, typename... Arg>
|
|
ConcreteAttrClass getTestElementsAttr(MLIRContext *ctx, ArrayRef<int64_t> shape,
|
|
Arg... value) {
|
|
auto eleType = FloatType::getF32(ctx);
|
|
ShapedType tensorType;
|
|
if (shape.size() == 1 && shape[0] == -1) {
|
|
tensorType = UnrankedTensorType::get(eleType);
|
|
} else {
|
|
tensorType = RankedTensorType::get(shape, eleType);
|
|
}
|
|
return ConcreteAttrClass::get(tensorType, value...);
|
|
}
|
|
|
|
ElementsAttr getTestSparseElementsAttr(MLIRContext *ctx,
|
|
ArrayRef<int64_t> shape) {
|
|
auto eleType = FloatType::getF32(ctx);
|
|
ShapedType tensorType;
|
|
if (shape.size() == 1 && shape[0] == -1) {
|
|
tensorType = UnrankedTensorType::get(eleType);
|
|
} else {
|
|
tensorType = RankedTensorType::get(shape, eleType);
|
|
}
|
|
auto indicesType = RankedTensorType::get({1, 2}, IntegerType::get(ctx, 64));
|
|
auto indices =
|
|
DenseIntElementsAttr::get(indicesType, {APInt(64, 0), APInt(64, 0)});
|
|
auto valuesType = RankedTensorType::get({1}, eleType);
|
|
auto values = DenseFPElementsAttr::get(valuesType, {APFloat(0.0f)});
|
|
return SparseElementsAttr::get(tensorType, indices, values);
|
|
}
|
|
|
|
UniformQuantizedType getTestQuantizedType(Type storageType, MLIRContext *ctx) {
|
|
return UniformQuantizedType::get(/*flags=*/false, storageType,
|
|
FloatType::getF32(ctx), /*scale=*/1.0,
|
|
/*zeroPoint=*/0, /*storageTypeMin=*/0,
|
|
/*storageTypeMax=*/255);
|
|
}
|
|
|
|
TEST(QuantizationUtilsTest, convertFloatAttrUniform) {
|
|
MLIRContext ctx;
|
|
ctx.getOrLoadDialect<QuantizationDialect>();
|
|
IntegerType convertedType = IntegerType::get(&ctx, 8);
|
|
auto quantizedType = getTestQuantizedType(convertedType, &ctx);
|
|
TestUniformQuantizedValueConverter converter(quantizedType);
|
|
|
|
auto realValue = getTestFloatAttr(1.0, &ctx);
|
|
Type typeResult;
|
|
auto valueResult =
|
|
quantizeAttrUniform(realValue, quantizedType, converter, typeResult);
|
|
|
|
EXPECT_EQ(valueResult.cast<IntegerAttr>().getInt(), 5);
|
|
EXPECT_EQ(
|
|
valueResult.cast<IntegerAttr>().getType().cast<IntegerType>().getWidth(),
|
|
convertedType.getWidth());
|
|
}
|
|
|
|
TEST(QuantizationUtilsTest, convertRankedDenseAttrUniform) {
|
|
MLIRContext ctx;
|
|
ctx.getOrLoadDialect<QuantizationDialect>();
|
|
IntegerType convertedType = IntegerType::get(&ctx, 8);
|
|
auto quantizedType = getTestQuantizedType(convertedType, &ctx);
|
|
TestUniformQuantizedValueConverter converter(quantizedType);
|
|
auto realValue = getTestElementsAttr<DenseElementsAttr, ArrayRef<Attribute>>(
|
|
&ctx, {1, 2}, {getTestFloatAttr(1.0, &ctx), getTestFloatAttr(2.0, &ctx)});
|
|
|
|
Type returnedType;
|
|
auto returnedValue =
|
|
quantizeAttrUniform(realValue, quantizedType, converter, returnedType);
|
|
|
|
// Check Elements attribute shape and kind are not changed.
|
|
auto tensorType = returnedType.cast<TensorType>();
|
|
auto expectedTensorType = realValue.getType().cast<TensorType>();
|
|
EXPECT_EQ(tensorType.getShape(), expectedTensorType.getShape());
|
|
EXPECT_EQ(tensorType.getElementType(), convertedType);
|
|
EXPECT_TRUE(returnedValue.isa<DenseIntElementsAttr>());
|
|
|
|
// Check Elements attribute element value is expected.
|
|
auto firstValue =
|
|
returnedValue.cast<ElementsAttr>().getValues<Attribute>()[{0, 0}];
|
|
EXPECT_EQ(firstValue.cast<IntegerAttr>().getInt(), 5);
|
|
}
|
|
|
|
TEST(QuantizationUtilsTest, convertRankedSplatAttrUniform) {
|
|
MLIRContext ctx;
|
|
ctx.getOrLoadDialect<QuantizationDialect>();
|
|
IntegerType convertedType = IntegerType::get(&ctx, 8);
|
|
auto quantizedType = getTestQuantizedType(convertedType, &ctx);
|
|
TestUniformQuantizedValueConverter converter(quantizedType);
|
|
auto realValue = getTestElementsAttr<DenseElementsAttr, Attribute>(
|
|
&ctx, {1, 2}, getTestFloatAttr(1.0, &ctx));
|
|
|
|
Type returnedType;
|
|
auto returnedValue =
|
|
quantizeAttrUniform(realValue, quantizedType, converter, returnedType);
|
|
|
|
// Check Elements attribute shape and kind are not changed.
|
|
auto tensorType = returnedType.cast<TensorType>();
|
|
auto expectedTensorType = realValue.getType().cast<TensorType>();
|
|
EXPECT_EQ(tensorType.getShape(), expectedTensorType.getShape());
|
|
EXPECT_EQ(tensorType.getElementType(), convertedType);
|
|
EXPECT_TRUE(returnedValue.isa<SplatElementsAttr>());
|
|
|
|
// Check Elements attribute element value is expected.
|
|
auto firstValue =
|
|
returnedValue.cast<ElementsAttr>().getValues<Attribute>()[{0, 0}];
|
|
EXPECT_EQ(firstValue.cast<IntegerAttr>().getInt(), 5);
|
|
}
|
|
|
|
TEST(QuantizationUtilsTest, convertRankedSparseAttrUniform) {
|
|
MLIRContext ctx;
|
|
ctx.getOrLoadDialect<QuantizationDialect>();
|
|
IntegerType convertedType = IntegerType::get(&ctx, 8);
|
|
auto quantizedType = getTestQuantizedType(convertedType, &ctx);
|
|
TestUniformQuantizedValueConverter converter(quantizedType);
|
|
auto realValue = getTestSparseElementsAttr(&ctx, {1, 2});
|
|
|
|
Type returnedType;
|
|
auto returnedValue =
|
|
quantizeAttrUniform(realValue, quantizedType, converter, returnedType);
|
|
|
|
// Check Elements attribute shape and kind are not changed.
|
|
auto tensorType = returnedType.cast<TensorType>();
|
|
auto expectedTensorType = realValue.getType().cast<TensorType>();
|
|
EXPECT_EQ(tensorType.getShape(), expectedTensorType.getShape());
|
|
EXPECT_EQ(tensorType.getElementType(), convertedType);
|
|
EXPECT_TRUE(returnedValue.isa<SparseElementsAttr>());
|
|
|
|
// Check Elements attribute element value is expected.
|
|
auto firstValue =
|
|
returnedValue.cast<ElementsAttr>().getValues<Attribute>()[{0, 0}];
|
|
EXPECT_EQ(firstValue.cast<IntegerAttr>().getInt(), 5);
|
|
}
|
|
|
|
} // end namespace
|