bench/Im2ColFusedRequantizeBenchmark.cc (296 lines of code) (raw):

/* * 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 <algorithm> #include <chrono> #include <cmath> #include <iomanip> #include <iostream> #include <random> #include <vector> #ifdef _OPENMP #include <omp.h> #endif #include "./BenchUtils.h" #include "fbgemm/Fbgemm.h" #include "src/RefImplementations.h" using namespace std; using namespace fbgemm; template <typename Acc_t> void performance_test() { vector<conv_param_t<>> shapes = { // MB, IC, OC, IH, IW, G, KH, KW, stride_h, stride_w, // pad_h_top, pad_w_left, pad_h_bottom, pad_w_right // ResNext 101 // Batch size = 1 conv_param_t<>(1, 3, 64, {224, 224}, 1, {7, 7}, {2, 2}, {3, 3, 3, 3}), conv_param_t<>(1, 3, 64, {320, 320}, 1, {7, 7}, {2, 2}, {3, 3, 3, 3}), conv_param_t<>(1, 3, 64, {320, 320}, 1, {3, 3}, {2, 2}, {1, 1, 1, 1}), conv_param_t<>(1, 128, 128, {56, 56}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>(1, 128, 128, {56, 56}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>(1, 256, 256, {56, 56}, 32, {3, 3}, {2, 2}, {1, 1, 1, 1}), conv_param_t<>(1, 256, 256, {28, 28}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>(1, 512, 512, {28, 28}, 32, {3, 3}, {2, 2}, {1, 1, 1, 1}), conv_param_t<>(1, 512, 512, {14, 14}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>(1, 512, 512, {14, 14}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>(1, 1024, 1024, {14, 14}, 32, {3, 3}, {2, 2}, {1, 1, 1, 1}), conv_param_t<>(1, 1024, 1024, {7, 7}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>(1, 1024, 1024, {7, 7}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), // Batch size = 50 conv_param_t<>(50, 3, 64, {224, 224}, 1, {7, 7}, {2, 2}, {3, 3, 3, 3}), conv_param_t<>(50, 128, 128, {56, 56}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>(50, 128, 128, {56, 56}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>(50, 256, 256, {56, 56}, 32, {3, 3}, {2, 2}, {1, 1, 1, 1}), conv_param_t<>(50, 256, 256, {28, 28}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>(50, 512, 512, {28, 28}, 32, {3, 3}, {2, 2}, {1, 1, 1, 1}), conv_param_t<>(50, 512, 512, {14, 14}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>(50, 512, 512, {14, 14}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>( 50, 1024, 1024, {14, 14}, 32, {3, 3}, {2, 2}, {1, 1, 1, 1}), conv_param_t<>(50, 1024, 1024, {7, 7}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), conv_param_t<>(50, 1024, 1024, {7, 7}, 32, {3, 3}, {1, 1}, {1, 1, 1, 1}), }; bool flush = true; std::vector<char> llc; if (flush) { llc.resize(128 * 1024 * 1024, 1.0); } constexpr int NWARMUP = 4; constexpr int NITER = 10; #ifdef FBGEMM_MEASURE_TIME_BREAKDOWN cout << "WARNING: the timer may be inaccurate when used by multiple threads." << endl; cout << "MB, " << "IC, " << "OC, " << "IH, " << "IW, " << "G, " << "KH, " << "KW, " << "stride_h, " << "stride_w, " << "pad_h, " << "pad_w, " << "Type, " << "M, " << "N, " << "K, " << "Im2Col (ms), " << "Packing (ms), " << "Kernel (ms), " << "Postprocessing (ms), " << "fbgemmPacked (ms), " << "Total (ms), " << "GOPS" << endl; #else cout << setw(8) << "MB, " << "IC, " << "OC, " << "IH, " << "IW, " << "G, " << "KH, " << "KW, " << "stride_h, " << "stride_w, " << "pad_h, " << "pad_w, " << "Type, " << "M, " << "N, " << "K, " << setw(5) << "GOPS" << endl; #endif chrono::time_point<chrono::high_resolution_clock> begin, end; for (auto conv_p : shapes) { if (conv_p.IC % conv_p.G != 0 || conv_p.OC % conv_p.G != 0) { continue; } aligned_vector<uint8_t> Aint8( conv_p.MB * conv_p.IN_DIM[0] * conv_p.IN_DIM[1] * conv_p.IC); aligned_vector<uint8_t> Aint8_out( conv_p.MB * conv_p.OUT_DIM[0] * conv_p.OUT_DIM[1] * conv_p.K[0] * conv_p.K[1] * conv_p.IC); aligned_vector<int8_t> Bint8( conv_p.K[0] * conv_p.K[1] * conv_p.IC * conv_p.OC); aligned_vector<int32_t> Cint32_ref( conv_p.MB * conv_p.OUT_DIM[0] * conv_p.OUT_DIM[1] * conv_p.OC); aligned_vector<int32_t> Cint32_fb(Cint32_ref.size()); aligned_vector<int32_t> Cint32_fb2(Cint32_ref.size()); // A matrix (input activations) randFill<uint8_t>(Aint8, 0, 5); int32_t Aint8_zero_point = 4; // B matrix (weights) randFill<int8_t>(Bint8, -4, 4); // int32_t Bint8_zero_point = -3; aligned_vector<float> Bfp32(Bint8.begin(), Bint8.end()); // reference implementation conv_ref( conv_p, Aint8.data(), Aint8_zero_point, Bint8.data(), Cint32_ref.data()); // matrix dimensions after im2col int MDim = conv_p.MB * conv_p.OUT_DIM[0] * conv_p.OUT_DIM[1]; int NDim = conv_p.OC / conv_p.G; int KDim = conv_p.K[0] * conv_p.K[1] * conv_p.IC; // printMatrix(matrix_op_t::NoTranspose, Bint8.data(), KDim, NDim, NDim, // "B unpacked"); // packedB.printPackedMatrix("B Packed"); double nops = 2.0 * static_cast<double>(NITER) * MDim * NDim * KDim; double ttot = 0.0; string runType; vector<int32_t> row_offset_buf( PackAWithIm2Col<uint8_t, Acc_t>::rowOffsetBufferSize()); PackAWithIm2Col<uint8_t, Acc_t> packA( conv_p, Aint8.data(), nullptr, Aint8_zero_point, row_offset_buf.data()); PackBMatrix<int8_t, Acc_t> packedB( matrix_op_t::NoTranspose, KDim, NDim, Bint8.data(), NDim, nullptr, conv_p.G); // no-op output process objects DoNothing<int32_t, int32_t> doNothing32BitObj; memCopy<> memcopyObj(doNothing32BitObj); runType = "FusedIm2Col"; ttot = 0; #ifdef FBGEMM_MEASURE_TIME_BREAKDOWN double im2col_time = 0.0; double total_im2col_time = 0.0; double total_packing_time = 0.0; double total_computing_time = 0.0; double total_kernel_time = 0.0; double total_postprocessing_time = 0.0; double total_run_time = 0.0; #endif for (auto i = 0; i < NWARMUP + NITER; ++i) { #ifdef FBGEMM_MEASURE_TIME_BREAKDOWN packing_time = 0.0; computing_time = 0.0; kernel_time = 0.0; postprocessing_time = 0.0; run_time = 0.0; #endif llc_flush(llc); begin = chrono::high_resolution_clock::now(); fbgemmPacked( packA, packedB, Cint32_fb.data(), Cint32_fb.data(), conv_p.G * NDim, memcopyObj, 0, 1); end = chrono::high_resolution_clock::now(); if (i >= NWARMUP) { auto dur = chrono::duration_cast<chrono::nanoseconds>(end - begin); ttot += dur.count(); #ifdef FBGEMM_MEASURE_TIME_BREAKDOWN total_packing_time += packing_time; total_computing_time += computing_time; total_kernel_time += kernel_time; total_postprocessing_time += postprocessing_time; total_run_time += run_time; #endif } } cout << setw(4) << conv_p.MB << ", " << conv_p.IC << ", " << conv_p.OC << ", " << conv_p.IN_DIM[0] << ", " << conv_p.IN_DIM[1] << ", " << conv_p.G << ", " << conv_p.K[0] << ", " << conv_p.K[1] << ", " << conv_p.stride[0] << ", " << conv_p.stride[1] << ", " << conv_p.pad[0] << ", " << conv_p.pad[1] << ", "; cout << setw(13) << runType << ", " << setw(5) << fixed << setw(5) << setw(6) << MDim << ", " << setw(6) << NDim << ", " << setw(6) << KDim << ", "; #ifdef FBGEMM_MEASURE_TIME_BREAKDOWN cout << fixed << setprecision(6) << setw(8) << 0 << ", " << total_packing_time / (double)NITER / 1e6 << ", " << total_kernel_time / (double)NITER / 1e6 << ", " << total_postprocessing_time / (double)NITER / 1e6 << ", " << total_run_time / (double)NITER / 1e6 << ", " << ttot / (double)NITER / 1e6 << ", "; #endif cout << setprecision(2) << nops / ttot << endl; compare_buffers(Cint32_ref.data(), Cint32_fb.data(), MDim, NDim, NDim, 5); runType = "UnfusedIm2Col"; ttot = 0; #ifdef FBGEMM_MEASURE_TIME_BREAKDOWN total_im2col_time = 0.0; total_packing_time = 0.0; total_computing_time = 0.0; total_kernel_time = 0.0; total_postprocessing_time = 0.0; total_run_time = 0.0; #endif for (auto i = 0; i < NWARMUP + NITER; ++i) { #ifdef FBGEMM_MEASURE_TIME_BREAKDOWN im2col_time = 0.0; packing_time = 0.0; computing_time = 0.0; kernel_time = 0.0; postprocessing_time = 0.0; run_time = 0.0; #endif llc_flush(llc); begin = chrono::high_resolution_clock::now(); im2col_ref(conv_p, Aint8.data(), Aint8_zero_point, Aint8_out.data()); #ifdef FBGEMM_MEASURE_TIME_BREAKDOWN end = chrono::high_resolution_clock::now(); im2col_time = chrono::duration_cast<chrono::nanoseconds>(end - begin).count(); #endif // printMatrix(matrix_op_t::NoTranspose, Aint8_out.data(), MDim, KDim, // KDim, "A_out after im2col unpacked"); PackAWithRowOffset<uint8_t, Acc_t> packAN( matrix_op_t::NoTranspose, MDim, KDim, Aint8_out.data(), KDim, nullptr, conv_p.G, row_offset_buf.data()); fbgemmPacked( packAN, packedB, Cint32_fb2.data(), Cint32_fb2.data(), conv_p.G * NDim, memcopyObj, 0, 1); end = chrono::high_resolution_clock::now(); if (i >= NWARMUP) { auto dur = chrono::duration_cast<chrono::nanoseconds>(end - begin); ttot += dur.count(); #ifdef FBGEMM_MEASURE_TIME_BREAKDOWN total_im2col_time += im2col_time; total_packing_time += packing_time; total_computing_time += computing_time; total_kernel_time += kernel_time; total_postprocessing_time += postprocessing_time; total_run_time += run_time; #endif } } if (flush) { ((volatile char*)(llc.data()))[0] += 1; } // packedB.printPackedMatrix("bench B Packed"); // printMatrix(matrix_op_t::NoTranspose, Cint32_fb.data(), MDim, NDim, NDim, // "C fb fp32"); // printMatrix(matrix_op_t::NoTranspose, Cint32_fb2.data(), // MDim, NDim, NDim, "C fb2 fp32"); // printMatrix(matrix_op_t::NoTranspose, // Cint32_ref.data(), MDim, NDim, NDim, "C ref fp32"); cout << setw(4) << conv_p.MB << ", " << conv_p.IC << ", " << conv_p.OC << ", " << conv_p.IN_DIM[0] << ", " << conv_p.IN_DIM[1] << ", " << conv_p.G << ", " << conv_p.K[0] << ", " << conv_p.K[1] << ", " << conv_p.stride[0] << ", " << conv_p.stride[1] << ", " << conv_p.pad[0] << ", " << conv_p.pad[1] << ", "; cout << setw(13) << runType << ", " << setw(5) << fixed << setw(5) << setw(6) << MDim << ", " << setw(6) << NDim << ", " << setw(6) << KDim << ", "; #ifdef FBGEMM_MEASURE_TIME_BREAKDOWN cout << fixed << setprecision(6) << setw(8) << total_im2col_time / (double)NITER / 1e6 << ", " << total_packing_time / (double)NITER / 1e6 << ", " << total_kernel_time / (double)NITER / 1e6 << ", " << total_postprocessing_time / (double)NITER / 1e6 << ", " << total_run_time / (double)NITER / 1e6 << ", " << ttot / (double)NITER / 1e6 << ", "; #endif cout << setprecision(2) << nops / ttot << endl; compare_buffers(Cint32_ref.data(), Cint32_fb2.data(), MDim, NDim, NDim, 5); } // shapes } int main() { #ifdef _OPENMP // Use 1 thread unless OMP_NUM_THREADS is explicit set. const char* val = getenv("OMP_NUM_THREADS"); if (val == nullptr || !*val) { omp_set_num_threads(1); } #endif performance_test<int16_t>(); performance_test<int32_t>(); return 0; }