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
e78815d
master
1// This compute shader implements yet another version of matrix*matrix product 2// For optimal VRAM access pattern, it requires both arguments to be reshaped into a sequence of horizontal column major panels. 3// The panel height is TILE_SIZE, and the last panel of the matrix needs to be padded with zeros; see matReshapePanels.hlsl shader for the reshaping. 4// So far, it's only used when running on AMD GPUs. 5#ifndef TILE_SIZE 6static const uint TILE_SIZE = 32 ; 7#endif 8#ifndef TILE_HEIGHT 9static const uint TILE_HEIGHT = 64 ; 10#endif 11#ifndef THREADS_Y 12static const uint THREADS_Y = 8 ; 13#endif 14// The above values have a following constraint: TILE_SIZE = THREADS_Y * N * 4 where N is an integer 15 16#ifndef STREAM_SECOND_MATRIX 17#define STREAM_SECOND_MATRIX 1 18#endif 19 20// First tensor, reshaped into dense column major horizontal panels of size [ width, TILE_SIZE ] 21Buffer < float > arg0 :register ( t0 ); 22// Second tensor, reshaped into dense column major horizontal panels of size [ width, TILE_SIZE ] 23Buffer < float > arg1 :register ( t1 ); 24// FP32 output tensor, row major and continuous 25RWBuffer < float > result :register ( u0 ); 26 27cbuffer Constants :register ( b0 ) 28{ 29uint4 arg0Size :packoffset ( c0 ); 30uint arg0panel :packoffset ( c1 . y ); 31uint2 arg0LayerStrides :packoffset ( c1 . z ); 32 33// uint4 arg1Size: packoffset( c2 ); 34uint arg1panel :packoffset ( c3 . y ); 35uint2 arg1LayerStrides :packoffset ( c3 . z ); 36 37uint4 resultSize :packoffset ( c4 ); 38uint4 resultStrides :packoffset ( c5 ); 39} 40 41// A smaller tile loaded from the first source matrix 42groupsharedfloat tile0 [ TILE_HEIGHT ][ TILE_SIZE ]; 43#if ! STREAM_SECOND_MATRIX 44// A smaller tile loaded from the second source matrix 45groupshared float tile1 [ TILE_HEIGHT ][ TILE_SIZE ]; 46#endif 47 48// Count of FP32 accumulators we need in every thread of the shader 49static const uint heightScalars = TILE_SIZE / THREADS_Y ; 50// The local accumulators are float4 vectors, compute count of these vectors 51static const uint heightVectors = ( heightScalars + 3 ) / 4 ; 52 53#if STREAM_SECOND_MATRIX 54void multiplyTiles ( const uint3 thread , uint rsi , const uint h , inout float4 acc [ heightVectors ] ) 55{ 56uint4 rsi4 = rsi + uint4 ( 0 , THREADS_Y , THREADS_Y * 2 , THREADS_Y * 3 ); 57[ unroll ] 58for ( uint iv = 0 ; iv < heightVectors ; iv ++ , rsi4 += THREADS_Y * 4 ) 59{ 60float4 r = 0.0 ; 61uint4 rsiRow = rsi4 ; 62for ( uint j = 0 ; j < h ; j ++ , rsiRow += TILE_SIZE ) 63{ 64const float a = tile0 [ j ][ thread . x ]; 65float4 b = 0.0 ; 66[ unroll ] 67for ( uint k = 0 ; k < 4 ; k ++ ) 68{ 69b [ k ] = arg1 [ rsiRow [ k ] ]; 70} 71r = mad ( a , b , r ); 72} 73acc [ iv ] += r ; 74} 75} 76#else 77void multiplyTiles ( const uint3 thread , inout float4 acc [ heightVectors ] ) 78{ 79[ unroll ] 80for ( uint i = 0 ; i < heightVectors ; i ++ ) 81{ 82float4 r = 0.0 ; 83for ( uint j = 0 ; j < TILE_HEIGHT ; j ++ ) 84{ 85const float a = tile0 [ j ][ thread . x ]; 86float4 b ; 87[ unroll ] 88for ( uint k = 0 ; k < 4 ; k ++ ) 89{ 90const uint row = ( i * 4 + k ) * THREADS_Y + thread . y ; 91b [ k ] = tile1 [ j ][ row ]; 92} 93r = mad ( a , b , r ); 94} 95acc [ i ] += r ; 96} 97} 98#endif 99 100void storeTile ( const uint3 thread , const uint4 pos , const uint2 size , in float4 acc [ heightVectors ] ) 101{ 102if ( thread . x >= size . x ) 103return ; 104 105const uint4 prod4 = pos * resultStrides ; 106const uint2 prod2 = prod4 . xy + prod4 . zw ; 107uint rdi = prod2 . x + prod2 . y ; 108rdi += resultStrides . y * thread . y ; 109rdi += resultStrides . x * thread . x ; 110 111const uint4 offsets = THREADS_Y * uint4 ( 0 , 1 , 2 , 3 ); //< a compile-time constant vector 112uint4 rdi4 = resultStrides . y * offsets + rdi ; 113 114[ unroll ] 115for ( uint iv = 0 ; iv < heightVectors ; iv ++ , rdi4 += resultStrides . y * THREADS_Y * 4 ) 116{ 117const float4 source = acc [ iv ]; 118[ unroll ] 119for ( uint k = 0 ; k < 4 ; k ++ ) 120{ 121const uint i = ( iv * 4 + k ) * THREADS_Y + thread . y ; 122if ( i < size . y ) 123result [ rdi4 [ k ] ] = source [ k ]; 124} 125} 126} 127 128[ numthreads ( TILE_SIZE , THREADS_Y , 1 )] 129void main ( const uint3 group : SV_GroupID , const uint3 thread : SV_GroupThreadID ) 130{ 131uint i ; 132// Zero all shared buffers 133for ( i = thread . y ; i < TILE_HEIGHT ; i += THREADS_Y ) 134{ 135tile0 [ i ][ thread . x ] = 0.0 ; 136#if ! STREAM_SECOND_MATRIX 137tile1 [ i ][ thread . x ] = 0.0 ; 138#endif 139} 140// Despite inside GPU cores, the shared memory is still much slower than registers 141// For this reason, this shader accumulates numbers in local variables. Only uses groupshared memory for tiles of the argument matrices. 142float4 acc [ heightVectors ]; 143// Zero out the accumulators 144[ unroll ] 145for ( i = 0 ; i < heightVectors ; i ++ ) 146acc [ i ] = 0.0 ; 147 148const uint2 layer = uint2 ( group . z % resultSize . z , group . z / resultSize . z ); 149 150uint rsi0 = group . x * arg0panel + layer . x * arg0LayerStrides . x + layer . y * arg0LayerStrides . y ; 151uint rsi1 = group . y * arg1panel + layer . x * arg1LayerStrides . x + layer . y * arg1LayerStrides . y ; 152 153const uint threadOffset = thread . y * TILE_SIZE + thread . x ; 154rsi0 += threadOffset ; 155#if STREAM_SECOND_MATRIX 156rsi1 += thread . y ; 157#else 158rsi1 += threadOffset ; 159#endif 160 161const uint completeTiles = arg0Size . x / TILE_HEIGHT ; 162for ( i = 0 ; i < completeTiles ; i ++ ) 163{ 164// Load [ TILE_SIZE, TILE_HEIGHT ] block from both source tensors into these groupshared buffers 165for ( uint j = thread . y ; j < TILE_HEIGHT ; j += THREADS_Y ) 166{ 167tile0 [ j ][ thread . x ] = arg0 [ rsi0 ]; 168rsi0 += THREADS_Y * TILE_SIZE ; 169#if ! STREAM_SECOND_MATRIX 170tile1 [ j ][ thread . x ] = arg1 [ rsi1 ]; 171rsi1 += THREADS_Y * TILE_SIZE ; 172#endif 173} 174 175// Wait for all threads in the group to complete these loads 176GroupMemoryBarrierWithGroupSync (); 177 178#if STREAM_SECOND_MATRIX 179multiplyTiles ( thread , rsi1 , TILE_HEIGHT , acc ); 180rsi1 += TILE_HEIGHT * TILE_SIZE ; 181#else 182// Multiply + accumulate the elements collected in the groupshared buffers 183multiplyTiles ( thread , acc ); 184#endif 185GroupMemoryBarrierWithGroupSync (); 186} 187 188const uint rem = arg0Size . x % TILE_HEIGHT ; 189if ( rem != 0 ) 190{ 191// Load [ TILE_SIZE, rem ] block from both source tensors, and zero out the padding elements 192for ( uint j = thread . y ; j < TILE_HEIGHT ; j += THREADS_Y ) 193{ 194[ branch ] 195if ( j < rem ) 196{ 197tile0 [ j ][ thread . x ] = arg0 [ rsi0 ]; 198rsi0 += THREADS_Y * TILE_SIZE ; 199#if ! STREAM_SECOND_MATRIX 200tile1 [ j ][ thread . x ] = arg1 [ rsi1 ]; 201rsi1 += THREADS_Y * TILE_SIZE ; 202#endif 203} 204else 205{ 206tile0 [ j ][ thread . x ] = 0.0 ; 207#if ! STREAM_SECOND_MATRIX 208tile1 [ j ][ thread . x ] = 0.0 ; 209#endif 210} 211} 212 213// Wait for all threads in the group to complete these loads 214GroupMemoryBarrierWithGroupSync (); 215 216// Multiply + accumulate the elements collected in the groupshared buffers 217#if STREAM_SECOND_MATRIX 218multiplyTiles ( thread , rsi1 , rem , acc ); 219#else 220multiplyTiles ( thread , acc ); 221#endif 222GroupMemoryBarrierWithGroupSync (); 223} 224 225const uint2 resultPos = group . xy * TILE_SIZE ; 226const uint2 outputSize = min ( TILE_SIZE , resultSize . xy - resultPos ); 227storeTile ( thread , uint4 ( resultPos , layer ), outputSize , acc ); 228}