### Install gemmlowp with vcpkg Source: https://github.com/google/gemmlowp/blob/master/README.md Use the vcpkg package manager to download and install gemmlowp. Ensure vcpkg is set up correctly before running the install command. ```bash git clone https://github.com/Microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install ./vcpkg install gemmlowp ``` -------------------------------- ### Gemmlowp Quantization Example Source: https://github.com/google/gemmlowp/blob/master/doc/quantization.md Refer to this file for example code demonstrating gemmlowp quantization. ```c++ doc/quantization_example.cc ``` -------------------------------- ### Build standalone Android toolchain Source: https://github.com/google/gemmlowp/blob/master/README.md Example of setting up a standalone Android toolchain using Clang 3.5. This is useful for troubleshooting compilation issues. ```bash export INSTALL_DIR=~/toolchains/clang-21-stl-gnu $NDK/build/tools/make-standalone-toolchain.sh \ --toolchain=arm-linux-androideabi-clang3.5 --platform=android-21 \ --install-dir=$INSTALL_DIR ``` ```bash export CXX="$INSTALL_DIR/bin/arm-linux-androideabi-g++ \ --sysroot=$INSTALL_DIR/sysroot" ``` -------------------------------- ### Install Gemmlowp Headers Source: https://github.com/google/gemmlowp/blob/master/contrib/CMakeLists.txt Installs various header files for gemmlowp into specific subdirectories under the installation's include directory. This includes headers for eight_bit_int_gemm, meta, public, profiling, internal, and fixedpoint modules. ```cmake install(FILES ${eight_bit_int_gemm_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gemmlowp/eight_bit_int_gemm) file(GLOB meta_headers "${gemmlowp_src}/meta/*.h") install(FILES ${meta_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gemmlowp/meta) file(GLOB public_headers "${gemmlowp_src}/public/*.h") install(FILES ${public_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gemmlowp/public) file(GLOB profile_headers "${gemmlowp_src}/profiling/*.h") install(FILES ${profile_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gemmlowp/profiling) file(GLOB internal_headers "${gemmlowp_src}/internal/*.h") install(FILES ${internal_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gemmlowp/internal) file(GLOB fixedpoint_headers "${gemmlowp_src}/fixedpoint/*.h") install(FILES ${fixedpoint_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gemmlowp/fixedpoint) ``` -------------------------------- ### Install Gemmlowp Targets Source: https://github.com/google/gemmlowp/blob/master/contrib/CMakeLists.txt Installs the gemmlowp and eight_bit_int_gemm targets, along with their export configuration for CMake's find_package. This makes the libraries available for other projects to link against. ```cmake install(TARGETS gemmlowp eight_bit_int_gemm EXPORT gemmlowp-config # support find_package(gemmlowp CONFIG) RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(EXPORT gemmlowp-config # export gemmlowp::gemmlowp NAMESPACE gemmlowp:: # gemmlowp::eight_bit_int_gemm DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/gemmlowp) ``` -------------------------------- ### Quantized Matrix Multiplication Workflow Source: https://context7.com/google/gemmlowp/llms.txt This C++ code provides a complete example of quantized matrix multiplication. It includes functions for choosing quantization parameters, quantizing multipliers, and performing the GEMM operation using gemmlowp. Ensure gemmlowp library is included. ```cpp #include #include #include "../public/gemmlowp.h" #include "../public/output_stages.h" // Quantization parameters structure struct QuantParams { float scale; std::uint8_t zero_point; }; // Choose quantization parameters for a given value range QuantParams ChooseQuantParams(float min_val, float max_val) { min_val = std::min(min_val, 0.f); max_val = std::max(max_val, 0.f); const float qmin = 0, qmax = 255; float scale = (max_val - min_val) / (qmax - qmin); float initial_zero_point = qmin - min_val / scale; std::uint8_t zero_point = static_cast( std::max(0.f, std::min(255.f, std::round(initial_zero_point)))); return {scale, zero_point}; } // Convert multiplier to fixed-point representation void QuantizeMultiplier(float real_multiplier, std::int32_t* quantized_multiplier, int* right_shift) { int s = 0; while (real_multiplier < 0.5f) { real_multiplier *= 2.0f; s++; } std::int64_t q = static_cast( std::round(real_multiplier * (1ll << 31))); if (q == (1ll << 31)) { q /= 2; s--; } *quantized_multiplier = static_cast(q); *right_shift = s; } int main() { const int M = 2, K = 4, N = 3; // Float matrices std::vector float_lhs = {0.1f, -0.2f, 0.3f, -0.4f, 0.5f, -0.6f, 0.7f, -0.8f}; std::vector float_rhs = {0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f}; // Find ranges and choose quantization parameters auto lhs_params = ChooseQuantParams(-0.8f, 0.7f); auto rhs_params = ChooseQuantParams(0.1f, 1.2f); auto result_params = ChooseQuantParams(-1.0f, 1.0f); // Quantize inputs std::vector uint8_lhs(M * K); std::vector uint8_rhs(K * N); for (int i = 0; i < M * K; i++) { float val = lhs_params.zero_point + float_lhs[i] / lhs_params.scale; uint8_lhs[i] = static_cast( std::max(0.f, std::min(255.f, std::round(val)))); } for (int i = 0; i < K * N; i++) { float val = rhs_params.zero_point + float_rhs[i] / rhs_params.scale; uint8_rhs[i] = static_cast( std::max(0.f, std::min(255.f, std::round(val)))); } // Prepare matrix maps gemmlowp::MatrixMap lhs(uint8_lhs.data(), M, K); gemmlowp::MatrixMap rhs(uint8_rhs.data(), K, N); std::vector uint8_result(M * N); gemmlowp::MatrixMap result(uint8_result.data(), M, N); // Compute quantized multiplier float real_multiplier = lhs_params.scale * rhs_params.scale / result_params.scale; std::int32_t quantized_multiplier; int right_shift; QuantizeMultiplier(real_multiplier, &quantized_multiplier, &right_shift); // Configure output pipeline gemmlowp::OutputStageQuantizeDownInt32ByFixedPoint quantize_stage; quantize_stage.result_fixedpoint_multiplier = quantized_multiplier; quantize_stage.result_shift = right_shift; quantize_stage.result_offset_after_shift = result_params.zero_point; gemmlowp::OutputStageSaturatingCastToUint8 cast_stage; auto output_pipeline = std::make_tuple(quantize_stage, cast_stage); // Perform quantized GEMM gemmlowp::GemmContext gemm_context; gemmlowp::GemmWithOutputPipeline( &gemm_context, lhs, rhs, &result, -static_cast(lhs_params.zero_point), -static_cast(rhs_params.zero_point), output_pipeline); // Dequantize result for verification std::vector float_result(M * N); for (int i = 0; i < M * N; i++) { float_result[i] = result_params.scale * (uint8_result[i] - result_params.zero_point); } return 0; } ``` -------------------------------- ### NEON32Kernel12x4Depth2Assuming12BitProducts Source: https://github.com/google/gemmlowp/blob/master/doc/less-than-8-bit.md This function is an example of a computation kernel optimized for less-than-8-bit cases on ARM NEON architectures. It assumes inputs are within a restricted range, allowing for the use of uint16 accumulators to potentially double arithmetic throughput. ```cpp #include "../kernel_neon.h" namespace gemmlowp { inline void NEON32Kernel12x4Depth2Assuming12BitProducts( const std::uint8_t* lhs_data, const std::uint8_t* rhs_data, std::int32_t* result_data, std::int32_t* bias_data, std::size_t count, std::size_t rhs_rows, std::size_t rhs_cols, std::int32_t* scratch_data) { // This is a placeholder for the actual implementation. // The actual implementation would involve NEON intrinsics for optimized // matrix multiplication using uint16 accumulators. // For example, it might load 12-bit values, perform multiplications, // accumulate into uint16 registers, and then periodically sum these // into uint32 accumulators. // The function name suggests it handles a 12x4 kernel with depth 2, // assuming 12-bit products. } } // namespace gemmlowp ``` -------------------------------- ### Perform Quantized GEMM with Output Pipeline Source: https://context7.com/google/gemmlowp/llms.txt Use GemmWithOutputPipeline for quantized matrix multiplication with uint8 inputs and configurable output processing. Ensure correct setup of matrix maps, offsets, and output stages for desired quantization. ```cpp #include "../public/gemmlowp.h" #include "../public/output_stages.h" // Define matrix dimensions const int rows = 64; const int depth = 128; const int cols = 32; // Allocate storage for matrices std::vector lhs_data(rows * depth); std::vector rhs_data(depth * cols); std::vector result_data(rows * cols); // Initialize input matrices with quantized values (0-255) // ... populate lhs_data and rhs_data ... // Create matrix maps (views into existing buffers) gemmlowp::MatrixMap lhs(lhs_data.data(), rows, depth); gemmlowp::MatrixMap rhs(rhs_data.data(), depth, cols); gemmlowp::MatrixMap result(result_data.data(), rows, cols); // Quantization parameters (offsets are negated zero-points) const int lhs_offset = -128; // zero_point for LHS const int rhs_offset = -128; // zero_point for RHS // Configure output pipeline with fixed-point quantization gemmlowp::OutputStageQuantizeDownInt32ByFixedPoint quantize_stage; quantize_stage.result_fixedpoint_multiplier = 1073741824; // ~0.5 in Q0.31 quantize_stage.result_shift = 1; quantize_stage.result_offset_after_shift = 128; // result zero_point gemmlowp::OutputStageSaturatingCastToUint8 cast_stage; auto output_pipeline = std::make_tuple(quantize_stage, cast_stage); // Create context and perform GEMM gemmlowp::GemmContext gemm_context; gemmlowp::GemmWithOutputPipeline( &gemm_context, lhs, rhs, &result, lhs_offset, rhs_offset, output_pipeline); // result_data now contains the quantized matrix product ``` -------------------------------- ### Define Gemmlowp INTERFACE Library Source: https://github.com/google/gemmlowp/blob/master/contrib/CMakeLists.txt Defines the main gemmlowp library as an INTERFACE library. It specifies include directories for both build and installation, and links against the eight_bit_int_gemm library. ```cmake add_library(gemmlowp INTERFACE) target_include_directories(gemmlowp INTERFACE $ $) target_link_libraries(gemmlowp INTERFACE eight_bit_int_gemm) ``` -------------------------------- ### Clamp Output Stage for ReLU6 Source: https://context7.com/google/gemmlowp/llms.txt Configures a clamp output stage to limit values between specified minimum and maximum bounds, useful for activation functions like ReLU or ReLU6. This example sets bounds for ReLU6, clamping to the uint8 range. ```cpp #include "../public/output_stages.h" // Clamp stage for ReLU6 activation (values between 0 and 6) // After quantization, if zero_point=0 and scale=0.0235, then 6.0 maps to ~255 gemmlowp::OutputStageClamp clamp_stage; clamp_stage.min = 0; // ReLU: no negative values clamp_stage.max = 255; // Clamp to uint8 range (or use 6/scale for ReLU6) // Complete pipeline with ReLU activation fused gemmlowp::OutputStageQuantizeDownInt32ByFixedPoint quantize_stage; quantize_stage.result_fixedpoint_multiplier = 1073741824; quantize_stage.result_shift = 1; quantize_stage.result_offset_after_shift = 128; gemmlowp::OutputStageSaturatingCastToUint8 cast_stage; // Order matters: quantize -> clamp -> cast auto pipeline = std::make_tuple(quantize_stage, clamp_stage, cast_stage); ``` -------------------------------- ### Initialize Bazel Workspace and Build Source: https://github.com/google/gemmlowp/blob/master/README.md Set up a Bazel workspace by creating an empty WORKSPACE file in the parent directory and then build all gemmlowp targets. ```bash cd gemmlowp/.. touch WORKSPACE bazel build gemmlowp:all ``` -------------------------------- ### Build with Bazel for Native Optimization Source: https://github.com/google/gemmlowp/blob/master/README.md Use Bazel's default optimization configuration for building gemmlowp targets. This is recommended for binaries not intended for distribution, as it leverages the host machine's native instruction set. ```bash bazel build --config=opt //gemmlowp:all ``` -------------------------------- ### Configure Math Helpers Test Executable Source: https://github.com/google/gemmlowp/blob/master/contrib/CMakeLists.txt Sets up the 'test_math_helpers' executable for testing math helper functions. It compiles the relevant test file and includes common test headers. ```cmake add_executable(test_math_helpers "${gemmlowp_src}/test/test_math_helpers.cc" ${gemmlowp_test_headers}) ``` -------------------------------- ### Build and run benchmark on Android Source: https://github.com/google/gemmlowp/blob/master/README.md Build and run the main benchmark on an Android device, disabling assertions for potentially better performance. The script expects the CXX environment variable to be set. ```bash ./scripts/test-android.sh test/benchmark.cc -DNDEBUG ``` -------------------------------- ### Build and Test on Android Source: https://github.com/google/gemmlowp/blob/master/README.md Execute a script to build and run a program on an Android device for development workflow. ```bash scripts/test-android.sh ``` -------------------------------- ### gemmlowp 3-Stage Computation Scheme Source: https://github.com/google/gemmlowp/blob/master/doc/design.md This pseudo-code illustrates gemmlowp's 3-stage computation: packing, kernel execution with accumulators, and unpacking. It highlights the use of int32 accumulators for low-precision computation. ```pseudocode allocate(some_lhs_L2_block); allocate(some_rhs_L2_block); // new: temp storage for int32 accums allocate(some_int32_accumulators_block); for (some_lhs_L2_block) { pack(some_lhs_L2_block); for (some_rhs_L2_block) { pack(some_rhs_L2_block); for (some_lhs_sub_block in some_lhs_L2_block) { for (some_rhs_sub_block in some_rhs_L2_block) { // new: pass int32 accums to kernel kernel(&some_int32_accumulators_block, some_lhs_sub_block, some_rhs_sub_block); } } // new: unpack int32 accums into destination matrix unpack(some_int32_accumulators_block); } } ``` -------------------------------- ### Bias Addition Output Stage Source: https://context7.com/google/gemmlowp/llms.txt Implements an output stage that adds a bias vector to accumulator values. The bias can be a row or column vector, depending on the VectorMap shape. This example demonstrates a column vector bias added to each row. ```cpp #include "../public/output_stages.h" #include "../public/map.h" // Bias vector storage (one value per output column) const int num_cols = 32; std::vector bias_data(num_cols); // ... populate bias values ... // Create bias vector map (column shape = broadcast across rows) gemmlowp::VectorMap bias_vector(bias_data.data(), num_cols); // Create bias addition stage gemmlowp::OutputStageBiasAddition< gemmlowp::VectorMap> bias_stage; bias_stage.bias_vector = bias_vector; // Complete pipeline: bias -> quantize -> cast gemmlowp::OutputStageQuantizeDownInt32ByFixedPoint quantize_stage; quantize_stage.result_fixedpoint_multiplier = 1073741824; quantize_stage.result_shift = 1; quantize_stage.result_offset_after_shift = 0; gemmlowp::OutputStageSaturatingCastToUint8 cast_stage; auto pipeline = std::make_tuple(bias_stage, quantize_stage, cast_stage); ``` -------------------------------- ### Create Standard Output Pipeline Source: https://context7.com/google/gemmlowp/llms.txt Creates a standard two-stage output pipeline for legacy quantization. Use this for simple use cases with the legacy quantization paradigm. ```cpp #include "../public/output_stages.h" // Create standard pipeline with legacy quantization parameters // Formula: ((input + result_offset) * result_mult_int + rounding) >> result_shift std::int32_t result_offset = 0; std::int32_t result_mult_int = 1; std::int32_t result_shift = 0; auto output_pipeline = gemmlowp::MakeStandardOutputPipeline( result_offset, result_mult_int, result_shift); // Use with GEMM gemmlowp::GemmContext gemm_context; gemmlowp::GemmWithOutputPipeline( &gemm_context, lhs, rhs, &result, lhs_offset, rhs_offset, output_pipeline); ``` -------------------------------- ### Configure Benchmark Executables Source: https://github.com/google/gemmlowp/blob/master/contrib/CMakeLists.txt Builds benchmark executables when testing is enabled. It sets up 'benchmark' and 'benchmark_all_sizes' targets, linking them against external libraries and applying specific compile options for the latter. ```cmake add_executable(benchmark "${gemmlowp_src}/test/benchmark.cc" ${gemmlowp_test_headers}) target_link_libraries(benchmark ${EXTERNAL_LIBRARIES}) add_executable(benchmark_all_sizes "${gemmlowp_src}/test/benchmark_all_sizes.cc" ${gemmlowp_test_headers}) target_compile_options(benchmark_all_sizes PRIVATE -DBENCHMARK_8bit -DBENCHMARK_QUICK) target_link_libraries(benchmark_all_sizes ${EXTERNAL_LIBRARIES}) ``` -------------------------------- ### Typical GemmWithOutputPipeline Call Source: https://github.com/google/gemmlowp/blob/master/doc/public.md This snippet demonstrates a typical call to GemmWithOutputPipeline. It specifies the template parameters for input and output scalar types and bit depth, along with the context, input matrices, output matrix, offsets, and the output pipeline object. ```cpp gemmlowp::GemmWithOutputPipeline( &gemm_context, uint8_lhs_matrix, uint8_rhs_matrix, &uint8_result_matrix, lhs_offset, rhs_offset, output_pipeline); ``` -------------------------------- ### Compile and run main unit test Source: https://github.com/google/gemmlowp/blob/master/README.md Compile the primary unit test file along with its dependencies. This requires setting the CXX environment variable to an Android toolchain's C++ compiler. ```bash export CXX=/some/toolchains/arm-linux-androideabi-4.8/bin/arm-linux-androideabi-g++ ``` ```bash ./scripts/test-android.sh \ test/test.cc \ eight_bit_int_gemm/eight_bit_int_gemm.cc \ test/test_data.cc ``` -------------------------------- ### Configure FixedPoint Test Executable Source: https://github.com/google/gemmlowp/blob/master/contrib/CMakeLists.txt Sets up the 'test_fixedpoint' executable for testing fixed-point arithmetic implementations. It compiles the fixedpoint test file and includes common test headers. ```cmake add_executable(test_fixedpoint "${gemmlowp_src}/test/test_fixedpoint.cc" ${gemmlowp_test_headers}) ``` -------------------------------- ### Run all Bazel tests for gemmlowp Source: https://github.com/google/gemmlowp/blob/master/README.md Execute all tests defined within the gemmlowp Bazel workspace. This assumes your Bazel workspace is already set up. ```bash bazel test gemmlowp:all ``` -------------------------------- ### Build with Bazel for x86 SSE 4.1 Source: https://github.com/google/gemmlowp/blob/master/README.md Compile gemmlowp targets using Bazel, ensuring SSE 4.1 support for optimized performance on x86 architectures. This command implies `-msse4.1` and other beneficial flags for native compilation. ```bash bazel build --copt=-msse4.1 //gemmlowp:all ``` -------------------------------- ### GEMV Kernel Logic with NEON Intrinsics Source: https://github.com/google/gemmlowp/blob/master/todo/fast-gemv.txt Illustrates the core steps for a NEON-accelerated GEMV kernel, including loading data, applying offsets, and performing multiply-accumulate operations. Assumes 16-bit integer operations for operands and 32-bit for accumulators to prevent overflow. ```c++ Load a few vectors of LHS (uint8), add the LHS offset (int16), store in a few vector registers in int16. Load a single value of RHS (uint8), add the RHS offset (int16), store in a vector register in int16. Use vmlalq_s16 to multiply-accumulate those int16’s into int32 accumulators ``` ```c++ You can first convert the uint8 to uint16 by a long-move instruction (vmovl_u8), reintrepret the results as int16, and then perform an ordinary int16 vector addition (vaddq_s16). Sad to have to do this as 2 ``` -------------------------------- ### Create and Use MatrixMap Views Source: https://context7.com/google/gemmlowp/llms.txt Utilize MatrixMap to create lightweight views of existing data buffers as matrices without data ownership. Supports row-major and column-major orders and provides element access via operator(). ```cpp #include "../public/map.h" // Allocate raw storage const int rows = 4; const int cols = 3; std::vector storage(rows * cols); // Create a column-major matrix map gemmlowp::MatrixMap matrix(storage.data(), rows, cols); // Access elements using (row, col) indexing matrix(0, 0) = 1.0f; // First element matrix(1, 0) = 2.0f; // Second row, first column matrix(0, 1) = 3.0f; // First row, second column // Query dimensions int num_rows = matrix.rows(); // 4 int num_cols = matrix.cols(); // 3 int stride = matrix.stride(); // 4 (for ColMajor: rows) // Get raw pointer to element at (row, col) float* ptr = matrix.data(2, 1); // Create a sub-block view (shares underlying storage) auto block = matrix.block(0, 0, 2, 2); // 2x2 block at top-left // Create const matrix map for read-only access gemmlowp::MatrixMap const_matrix(storage.data(), rows, cols); ``` -------------------------------- ### Quantize Down Int32 By Fixed Point Output Stage Source: https://context7.com/google/gemmlowp/llms.txt Configures an output stage for converting 32-bit accumulator values to a target quantized scale using fixed-point multiplication. The result is then combined with a saturating cast for uint8 output. ```cpp #include "../public/output_stages.h" // This stage computes: // result = ((FixedPointMul(input, multiplier) + rounding) >> shift) + offset gemmlowp::OutputStageQuantizeDownInt32ByFixedPoint quantize_stage; // Fixed-point multiplier in Q0.31 format (representing value in [0.5, 1.0)) // For a real multiplier of 0.7, compute: round(0.7 * 2^31) = 1503238553 quantize_stage.result_fixedpoint_multiplier = 1503238553; // Right shift to apply after fixed-point multiplication // Combined with multiplier, achieves: real_multiplier = multiplier * 2^(-31-shift) quantize_stage.result_shift = 2; // Offset added after shifting (the result zero_point) quantize_stage.result_offset_after_shift = 128; // Combine with saturating cast for complete uint8 output gemmlowp::OutputStageSaturatingCastToUint8 saturating_cast; auto pipeline = std::make_tuple(quantize_stage, saturating_cast); ``` -------------------------------- ### GEMM Pseudo-code Overview Source: https://github.com/google/gemmlowp/blob/master/doc/design.md This pseudo-code outlines the general structure of a GEMM implementation, emphasizing block processing and packing for cache efficiency. ```pseudocode allocate(some_lhs_L2_block); allocate(some_rhs_L2_block); for (some_lhs_L2_block) { pack(some_lhs_L2_block); for (some_rhs_L2_block) { pack(some_rhs_L2_block); for (some_lhs_sub_block in some_lhs_L2_block) { for (some_rhs_sub_block in some_rhs_L2_block) { kernel(some_lhs_sub_block, some_rhs_sub_block); } } } } ``` -------------------------------- ### Configure Gemmlowp Test Executable Source: https://github.com/google/gemmlowp/blob/master/contrib/CMakeLists.txt Creates the 'test_gemmlowp' executable for running Gemmlowp tests. It compiles the main test file and test data file, linking against the eight_bit_int_gemm library. ```cmake add_executable(test_gemmlowp "${gemmlowp_src}/test/test.cc" "${gemmlowp_src}/test/test_data.cc" ${gemmlowp_test_headers}) target_link_libraries(test_gemmlowp eight_bit_int_gemm) ``` -------------------------------- ### Configure Allocator Test Executable Source: https://github.com/google/gemmlowp/blob/master/contrib/CMakeLists.txt Creates the 'test_allocator' executable for testing the memory allocator functionality. It compiles the allocator test file and includes common test headers. ```cmake add_executable(test_allocator "${gemmlowp_src}/test/test_allocator.cc" ${gemmlowp_test_headers}) ``` -------------------------------- ### Enable and Add Tests Source: https://github.com/google/gemmlowp/blob/master/contrib/CMakeLists.txt Enables CTest for the project and adds all defined test executables to the testing suite. This allows tests to be run using the 'make test' command. ```cmake enable_testing() foreach(testname "test_math_helpers" "test_blocking_counter" "test_allocator" "test_fixedpoint" "test_gemmlowp") add_test(NAME ${testname} COMMAND "${testname}") endforeach(testname) ``` -------------------------------- ### GemmWithOutputPipeline API Source: https://github.com/google/gemmlowp/blob/master/doc/public.md The GemmWithOutputPipeline function is the main entry point for performing low-precision matrix multiplication with a flexible output pipeline. ```APIDOC ## GemmWithOutputPipeline ### Description This function performs a low-precision matrix multiplication with a flexible output pipeline for post-processing the result. It supports pre-processing of operands with offsets and flexible post-processing through an output pipeline. ### Method `void` (Function Call) ### Endpoint N/A (C++ function) ### Parameters #### Template Parameters * **InputScalar** (type) - Required - The scalar type of the LHS and RHS operands. Currently must be `std::uint8_t`. * **OutputScalar** (type) - Required - The scalar type of the result. Currently must be `std::uint8_t`. * **BitDepthParams** (type) - Required - Defines the bit format and accuracy. `gemmlowp::DefaultL8R8BitDepthParams` is the recommended value. * **LhsOrder** (type) - Optional - Storage order of the LHS matrix (e.g., `RowMajor`, `ColMajor`). * **RhsOrder** (type) - Optional - Storage order of the RHS matrix. * **ResultOrder** (type) - Optional - Storage order of the result matrix. * **OutputPipelineType** (type) - Optional - The type of the output pipeline tuple. * **GemmContextType** (type) - Optional - The type of the context parameter. Currently must be `gemmlowp::GemmContext`. #### Function Parameters * **context** (`GemmContextType*`) - Required - Pointer to the `gemmlowp::GemmContext` object. * **lhs** (`const MatrixMap&`) - Required - The LHS operand matrix map. * **rhs** (`const MatrixMap&`) - Required - The RHS operand matrix map. * **result** (`MatrixMap*`) - Required - Pointer to the destination `MatrixMap` object. * **lhs_offset** (`int`) - Required - Constant added to each entry in the LHS matrix. * **rhs_offset** (`int`) - Required - Constant added to each entry in the RHS matrix. * **output_pipeline** (`const OutputPipelineType&`) - Required - A tuple of output stages specifying the output pipeline. ### Request Example ```cpp gemmlowp::GemmWithOutputPipeline( &gemm_context, uint8_lhs_matrix, uint8_rhs_matrix, &uint8_result_matrix, lhs_offset, rhs_offset, output_pipeline); ``` ### Response #### Success Response (void) This function does not return a value. The result is written to the `result` MatrixMap. #### Response Example N/A ``` -------------------------------- ### GemmWithOutputPipeline Function Prototype Source: https://github.com/google/gemmlowp/blob/master/doc/public.md This is the prototype for the GemmWithOutputPipeline function, which performs low-precision matrix multiplication with a flexible output pipeline. It is a template function with several type parameters defining the input and output scalar types, bit depth parameters, matrix storage orders, output pipeline type, and context type. ```cpp template void GemmWithOutputPipeline(GemmContextType* context, const MatrixMap& lhs, const MatrixMap& rhs, MatrixMap* result, int lhs_offset, int rhs_offset, const OutputPipelineType& output_pipeline); ``` -------------------------------- ### Naive Requantization (Multiplication/Division) Source: https://github.com/google/gemmlowp/blob/master/doc/less-than-8-bit.md This method approximates requantization by multiplying and dividing. It's biased due to rounding towards zero. ```c++ dst = (src * ((1 << N) - 1)) / 255; ``` -------------------------------- ### Gemmlowp Quantized Matrix Multiplication Output Calculation Source: https://github.com/google/gemmlowp/blob/master/doc/quantization.md This equation shows how the final quantized result is obtained by scaling the int32 accumulator and adding the result zero point. The scaling factor involves the scales of the left-hand side, right-hand side, and result matrices. ```cpp result_quantized_value = result_zero_point + (lhs_scale * rhs_scale / result_scale) * int32_accumulator (7) ``` -------------------------------- ### Single-Thread GEMM Implementation in gemmlowp Source: https://github.com/google/gemmlowp/blob/master/doc/design.md This C++ code snippet illustrates a single-threaded General Matrix Multiplication (GEMM) implementation within the gemmlowp library. It iterates through blocks of rows and columns, packing the left-hand side (lhs) matrix, packing the right-hand side (rhs) matrix (conditionally if not already packed), computing the result block, and then unpacking the result. ```cpp for (int r = 0; r < rows; r += block_params.l2_rows) { int rs = std::min(block_params.l2_rows, rows - r); PackLhs(&packed_lhs, lhs.block(r, 0, rs, depth)); for (int c = 0; c < cols; c += block_params.l2_cols) { int cs = std::min(block_params.l2_cols, cols - c); if (!pack_rhs_once) { PackRhs(&packed_rhs, rhs.block(0, c, depth, cs)); } Compute(kernel, block_params, &packed_result, packed_lhs, packed_rhs); auto result_block = result->block(r, c, rs, cs); UnpackResult(&result_block, packed_result, packed_lhs, packed_rhs, depth, result_offset, result_mult_int, result_shift); } } ``` -------------------------------- ### Naive Requantization (Bit Shifting) Source: https://github.com/google/gemmlowp/blob/master/doc/less-than-8-bit.md This method approximates requantization by shifting bits. It's biased because it approximates ((2^N) - 1) / 255 by (2^N) / 256. ```c++ dst = src >> (8 - N); ``` -------------------------------- ### Gemmlowp Kernel Accumulation Loop (Original) Source: https://github.com/google/gemmlowp/blob/master/doc/quantization.md This is the original form of the kernel accumulation loop before zero point optimization. It involves subtracting zero points from quantized values before multiplication. ```cpp int32_accumulator = Sum_over_i( (lhs_quantized_value[i] - lhs_zero_point) * (rhs_quantized_value[i] - rhs_zero_point) ) ``` -------------------------------- ### Expansion of Matrix Product with Offsets Source: https://github.com/google/gemmlowp/blob/master/doc/low-precision.md The expanded form of the matrix product with offsets, showing the four component terms that need to be computed. ```mathematica lhs * rhs \ + lhs_offset * P * rhs + lhs * rhs_offset * Q + lhs_offset * rhs_offset * P * Q ```