yum-archive/TaSTT-Whisper
High-performance GPGPU inference of OpenAI's Whisper automatic speech recognition (ASR) model
git clone https://git.yummers.dev/yum-archive/TaSTT-Whisper
8c4603c
master
1#include "stdafx.h" 2#include <intrin.h> 3#include "mulMatImpl.h" 4#include "mulMat.kernel.hpp" 5 6#define DBG_TRACK_TEMPLATE_INSTANTIATION 0 7 8#if DBG_TRACK_TEMPLATE_INSTANTIATION 9#include <unordered_set> 10static std::unordered_set < uint16_t > g_mulMatTemplates ; 11#endif 12 13namespace 14{ 15using namespace CpuCompute ; 16 17bool checkAvx2Support () 18 { 19int cpuInfo [4 ]; 20__cpuid (cpuInfo ,7 ); 21return (cpuInfo [1 ]& (1 <<5 ) )!= 0 ; 22 } 23 24// a / b, rounded up to the next integer 25inline uint32_t divRoundUp (uint32_t a ,uint32_t b ) 26 { 27assert (b != 0 ); 28return (a + (b - 1 ) ) /b ; 29 } 30} 31 32const bool MulMatBase ::haveAvx2 = checkAvx2Support (); 33 34MulMatBase ::MulMatBase (Tensor & result ,const Tensor & a ,const Tensor & b ,ParallelForRunner & pfor ,uint8_t panelHeightRegs ,uint8_t tileWidthFloats ) : 35resultPointer (result .fp32 () ), 36pa (a .data () ), 37pb (b .data () ), 38runner (pfor ) 39{ 40length = a .ne [0 ]; 41resultStrides [0 ]= result .nb [1 ]; 42resultStrides [1 ]= result .nb [2 ]; 43resultStrides [2 ]= result .nb [3 ]; 44store (resultSize ,result .sizeVec () ); 45store (stridesA ,a .stridesVec () ); 46store (stridesB ,b .stridesVec () ); 47 48countPanels = divRoundUp (resultSize [0 ],panelHeightRegs * 8 ); 49completeTilesPerPanel = resultSize [1 ] /tileWidthFloats ; 50lastColumnsInPanel = (uint8_t )(resultSize [1 ] %tileWidthFloats ); 51this -> panelHeightRegisters = panelHeightRegs ; 52this -> tileWidth = tileWidthFloats ; 53 54// Pick a method which reshapes a panel of the matrix A into the shape we need to compute the product 55// Store the pointer to that method in the field of this class 56if (a .nb [0 ]== 1 ) 57 { 58if (haveAvx2 ) 59pfnMakePanel = & MulMatBase ::transposePanelAvx2 ; 60else 61pfnMakePanel = & MulMatBase ::transposePanel ; 62 } 63else if (a .nb [1 ]== 1 ) 64 { 65switch (panelHeightRegs ) 66 { 67case 1 : 68pfnMakePanel = & MulMatBase ::copyPanelColumnMajor8 ; 69break ; 70case 2 : 71pfnMakePanel = & MulMatBase ::copyPanelColumnMajor16 ; 72break ; 73case 4 : 74pfnMakePanel = & MulMatBase ::copyPanelColumnMajor32 ; 75break ; 76default : 77throw E_NOTIMPL ; 78 } 79 } 80else 81pfnMakePanel = & MulMatBase ::gatherPanel ; 82 83// That last version is generic and very simple, unlikely to have weird bugs 84// pfnMakePanel = &MulMatBase::gatherPanel; 85 86#if DBG_TRACK_TEMPLATE_INSTANTIATION 87uint16_t key = panelHeightRegs ; 88key = key <<8 ; 89key |=tileWidthFloats ; 90if ( !g_mulMatTemplates .emplace (key ).second ) 91return ; 92logDebug (u8"MulMatImpl<panelHeightRegs = %i, tileWidthFloats = %i>" , (int )panelHeightRegs , (int )tileWidthFloats ); 93#endif 94} 95 96HRESULT MulMatBase ::run (ParallelForRunner & pfor ) 97{ 98size_t length = (size_t )countPanels * resultSize [2 ]* resultSize [3 ]; 99return pfor .parallelFor (* this ,length ); 100} 101 102const float * MulMatBase ::getLayerB (size_t m2 ,size_t m3 )const 103{ 104const float * rsi = (const float * )this -> pb ; 105rsi += m2 * stridesB [2 ]; 106rsi += m3 * stridesB [3 ]; 107return rsi ; 108} 109 110// This method is the main one, it�s called by the thread pool 111template < uint8_t panelHeightRegs ,uint8_t tileWidthFloats > 112HRESULT __stdcallMulMatImpl < panelHeightRegs ,tileWidthFloats > ::compute (size_t i ,size_t end )const noexcept 113{ 114// Allocate a thread-local buffer for the transposed panel 115constexpr size_t panelHeightFloats = panelHeightRegs * 8 ; 116uint16_t * const panel = (uint16_t * )runner .threadLocalBuffer (floatsPerPanel ()* 2 ); 117const size_t resultStride = resultStrides [0 ]; 118 119// Load a few numbers from this class into local variables, while upcasting from DWORD into size_t 120const size_t length = this -> length ; 121const std::array < size_t ,2 > stridesB {this -> stridesB [0 ],this -> stridesB [1 ] }; 122 123// This outer loop iterates over the panels assigned to the current thread 124// For example, matrix A of size [ 1024, 1024 ] may be split into panels of size [ 1024, 16 ] 125// Each iteration of that loop computes matrix product of that panel, with the complete matrix B 126for ( ;i < end ;i ++ ) 127 { 128const size_t iPanel = i %countPanels ; 129size_t j = i /countPanels ; 130const size_t m2 = j % (size_t )resultSize [2 ]; 131const size_t m3 = j / (size_t )resultSize [2 ]; 132 133CHECK ( (this ->*pfnMakePanel )(panel ,iPanel ,m2 ,m3 ) ); 134// We got a column-major panel in the thread local buffer, of size [ length, panelHeightRegs * 8 ] 135// Hopefully, these buffers should all fit at least in L3 cache 136// The longest matrix I saw in the debugger had 4096 elements, with panelHeightRegs = 4 that's 256 kb of data in the panel 137const float * pb = getLayerB (m2 ,m3 ); 138float * rdi = getPanelDest (iPanel ,m2 ,m3 ); 139 140const size_t storeWidth = std::min (panelHeightFloats , (size_t )resultSize [0 ]- iPanel * panelHeightFloats ); 141 std::array < __m256 ,panelHeightRegs > vecPanel ; 142#if 1 143ResultTile < panelHeightRegs ,tileWidthFloats > tile ; 144 145// This loop iterates over tiles within the panel. 146// Each iteration of the loop computes an output tile of the result matrix. 147for (j = 0 ;j < completeTilesPerPanel ;j ++ ,pb += tileWidthFloats * stridesB [1 ],rdi += resultStride * tileWidthFloats ) 148 { 149setZero (tile .arr ); 150const uint16_t * rsiA = panel ; 151const uint16_t * const rsiAEnd = panel + length * panelHeightFloats ; 152const float * rsiB = pb ; 153// This loop runs for `length` iterations, iterates over the first dimensions of both matrices, accumulating these dot products we're after 154for ( ;rsiA < rsiAEnd ;rsiA += panelHeightFloats ,rsiB += stridesB [0 ] ) 155 { 156loadPanel (rsiA ,vecPanel ); 157tile .kernel (vecPanel ,rsiB ,stridesB [1 ] ); 158 } 159tile .store (rdi ,storeWidth ,tileWidthFloats ,resultStride ); 160 } 161 162if (0 != lastColumnsInPanel ) 163 { 164setZero (tile .arr ); 165const uint16_t * rsiA = panel ; 166const uint16_t * rsiAEnd = panel + length * panelHeightFloats ; 167const float * rsiB = pb ; 168for ( ;rsiA < rsiAEnd ;rsiA += panelHeightFloats ,rsiB += stridesB [0 ] ) 169 { 170loadPanel (rsiA ,vecPanel ); 171tile .kernelPartial (vecPanel ,rsiB ,stridesB [1 ],lastColumnsInPanel ); 172 } 173tile .store (rdi ,storeWidth ,lastColumnsInPanel ,resultStride ); 174 } 175#else 176// This version bypasses horizontal tiling, instead implements a brute force algorithm to multiply the current panel by the complete B matrix 177// Not terribly efficient, only implemented for debugging purposes 178const size_t resHeight = resultSize [1 ]; 179 std::array < __m256 ,panelHeightRegs > tile ; 180for (size_t j = 0 ;j < resHeight ;j ++ ,pb += stridesB [1 ],rdi += resultStride ) 181 { 182setZero (tile ); 183 184const uint16_t * rsiA = panel ; 185const uint16_t * const rsiAEnd = panel + length * panelHeightFloats ; 186const float * rsiB = pb ; 187for (size_t k = 0 ;k < length ;k ++ ,rsiA += panelHeightFloats ,rsiB += stridesB [0 ] ) 188 { 189loadPanel (rsiA ,vecPanel ); 190const __m256 b = _mm256_broadcast_ss (rsiB ); 191for (size_t r = 0 ;r < panelHeightRegs ;r ++ ) 192tile [r ]= _mm256_fmadd_ps (vecPanel [r ],b ,tile [r ] ); 193 } 194 195 alignas(32 ) std::array < float ,panelHeightFloats > arr ; 196for (size_t k = 0 ;k < panelHeightRegs ;k ++ ) 197_mm256_store_ps (& arr [k * 8 ],tile [k ] ); 198memcpy (rdi ,arr .data (),storeWidth * 4 ); 199 } 200#endif 201 } 202return S_OK ; 203} 204 205// Instantiate the templates we need 206template class MulMatImpl < 4 ,1 > ; 207template class MulMatImpl < 1 , 1 > ; 208template class MulMatImpl < 4 , 2 > ; 209template class MulMatImpl < 1 , 2 > ; 210template class MulMatImpl < 2 , 3 > ; 211template class MulMatImpl < 1 , 3 > ; 212template class MulMatImpl < 2 , 4 > ; 213template class MulMatImpl < 1 , 4 > ;