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
15dbcac
master
1#include "stdafx.h" 2#include "MlContext.h" 3#include "testUtils.h" 4using namespace DirectCompute ; 5 6Tensor MlContext ::createTensor (eDataType type ,const std::array < uint32_t ,4 >& ne ) 7{ 8Tensor res ; 9check (res .create (type ,ne ) ); 10return res ; 11} 12 13Tensor MlContext ::createTensor (eDataType type , std::initializer_list < uint32_t > ne ) 14{ 15size_t nDims = ne .size (); 16if (0 == nDims || nDims > 4 ) 17throw E_INVALIDARG ; 18 std::array < uint32_t ,4 > arr ; 19for (size_t i = 0 ;i < nDims ;i ++ ) 20arr [i ]= ne .begin ()[i ]; 21for (size_t i = nDims ;i < 4 ;i ++ ) 22arr [i ]= 1 ; 23return createTensor (type ,arr ); 24} 25 26Tensor MlContext ::conv_1d_1s (const Tensor & a ,const Tensor & b ) 27{ 28assert (b .isMatrix () ); 29assert (a .ne [1 ]== b .ne [1 ] ); 30assert (a .ne [3 ]== 1 ); 31 32Tensor res = createTensor ( eDataType::FP32 , {b .ne [0 ],a .ne [2 ] } ); 33 34convolution (a ,b ,res ); 35return res ; 36} 37 38Tensor MlContext ::conv_1d_2s (const Tensor & a ,const Tensor & b ) 39{ 40assert (b .isMatrix () ); 41assert (a .ne [1 ]== b .ne [1 ] ); 42assert (a .ne [3 ]== 1 ); 43 44Tensor res = createTensor ( eDataType::FP32 , {b .ne [0 ] /2 ,a .ne [2 ] } ); 45#if 0 46static PrintUniqueTensorSizes printSize ("conv_1d_2s" ); 47printSize .a ,b ); 48#endif 49convolution2 (a ,b ,res ); 50return res ; 51} 52 53namespace 54{ 55inline bool canRepeat (const TensorShape & t0 ,const TensorShape & t1 ) 56 { 57return (t1 .ne [0 ] %t0 .ne [0 ]== 0 )&& 58 (t1 .ne [1 ] %t0 .ne [1 ]== 0 )&& 59 (t1 .ne [2 ] %t0 .ne [2 ]== 0 )&& 60 (t1 .ne [3 ] %t0 .ne [3 ]== 0 ); 61 } 62} 63 64Tensor MlContext ::cwiseBinary (const Tensor & a ,const Tensor & b ,eComputeShader cs ) 65{ 66assert (isSameShape (a ,b ) ); 67Tensor res = createTensor (a .getType (),a .ne ); 68cwiseBinary (a ,b ,res ,cs ); 69return res ; 70} 71 72Tensor __declspec(noinline )MlContext ::view2d (const Tensor & a ,uint32_t ne0 ,uint32_t ne1 ,uint32_t nb1 ,uint32_t offset ) 73{ 74if (0 != offset ) 75throw E_NOTIMPL ; 76 77Tensor res = a ; 78res .ne = {ne0 ,ne1 ,1 ,1 }; 79 80res .nb [1 ]= nb1 ; 81res .nb [2 ]= res .nb [3 ]= nb1 * ne1 ; 82return res ; 83} 84 85Tensor MlContext ::transpose (const Tensor & a ) 86{ 87Tensor result ; 88 89// A magic number for _mm_shuffle_epi32 SSE2 instruction to swap two lower int32 lanes in a vector 90constexpr int swapXy = _MM_SHUFFLE (3 ,2 ,0 ,1 ); 91 92__m128i v = a .sizeVec (); 93v = _mm_shuffle_epi32 (v ,swapXy ); 94store (result .ne ,v ); 95 96v = a .stridesVec (); 97v = _mm_shuffle_epi32 (v ,swapXy ); 98store (result .nb ,v ); 99 100result .setGpuViews (a ,a ); 101return result ; 102} 103 104Tensor MlContext ::norm (const Tensor & a ) 105{ 106Tensor res = createTensor (a .getType (),a .ne ); 107norm (a ,res ); 108return res ; 109} 110 111Tensor MlContext ::mulMat (const Tensor & a ,const Tensor & b ) 112{ 113if ( !canMulMat (a ,b ) ) 114throw E_INVALIDARG ; 115Tensor res = createTensor ( eDataType::FP32 , {a .ne [1 ],b .ne [1 ],a .ne [2 ],b .ne [3 ] } ); 116if constexpr (enableInexactOptimizations ) 117mulMatTiled (a ,b ,res ); 118else 119mulMat (a ,b ,res ); 120#if 0 121Tensor testTiled ; 122check (testTiled .create ( eDataType::FP32 ,res .ne ) ); 123mulMatTiled (a ,b ,testTiled ); 124 125 std::vector < float > current ,tiled ; 126res .download (current ); 127testTiled .download (tiled ); 128sTensorDiff diff = computeDiff (current .data (),tiled .data (),current .size () ); 129diff ."mulMatTiled" ); 130#endif 131return res ; 132} 133 134Tensor MlContext ::mulMatEx (const Tensor & a ,const Tensor & b ,const char * tagName ) 135{ 136if ( !canMulMat (a ,b ) ) 137throw E_INVALIDARG ; 138if (0 != a .nb [0 ] ) 139throw E_INVALIDARG ;// The first argument is expected to be pre-transposed 140 141const uint16_t tag = profiler .setNextTag (tagName ); 142 143if (b .ne [1 ]!= 1 ) 144 { 145if (b .nb [0 ]!= 0 ) 146 { 147Tensor rhs = reshapePanels (b ); 148profiler .setNextTag (tag ); 149return mulMatTiledEx (a ,rhs ); 150 } 151else 152 { 153// Second argument already reshaped into these panels 154return mulMatTiledEx (a ,b ); 155 } 156 } 157else 158 { 159if (0 != b .nb [0 ] ) 160return mulMatByRowTiledEx (a ,b ); 161 162// That shader requires classic VRAM layout of the second argument, gonna fail with pre-transposed one 163throw E_INVALIDARG ; 164 } 165} 166 167Tensor MlContext ::permute (const Tensor & a ,uint8_t axis0 ,uint8_t axis1 ,uint8_t axis2 ,uint8_t axis3 ) 168{ 169assert (axis0 < 4 ); 170assert (axis1 < 4 ); 171assert (axis2 < 4 ); 172assert (axis3 < 4 ); 173 174assert (axis0 != axis1 ); 175assert (axis0 != axis2 ); 176assert (axis0 != axis3 ); 177assert (axis1 != axis2 ); 178assert (axis1 != axis3 ); 179assert (axis2 != axis3 ); 180 181Tensor res = a ; 182res .ne [axis0 ]= a .ne [0 ]; 183res .ne [axis1 ]= a .ne [1 ]; 184res .ne [axis2 ]= a .ne [2 ]; 185res .ne [axis3 ]= a .ne [3 ]; 186 187res .nb [axis0 ]= a .nb [0 ]; 188res .nb [axis1 ]= a .nb [1 ]; 189res .nb [axis2 ]= a .nb [2 ]; 190res .nb [axis3 ]= a .nb [3 ]; 191return res ; 192} 193 194Tensor MlContext ::flashAttention (const Tensor & q ,const Tensor & k ,const Tensor & v ,bool masked ) 195{ 196if ( !canMulMat (k ,q ) ) 197throw E_INVALIDARG ; 198 199if constexpr (enableInexactOptimizations ) 200 { 201if ( !masked ) 202 { 203profiler .setNextTag ("flashAttn.1" ); 204Tensor tmp = mulMat (k ,q ); 205 206profiler .setNextTag ("flashAttention" ); 207const float tempScale = (float )(1.0 /sqrt ( (double )(int )q .ne [0 ] ) ); 208softMax (tmp ,tempScale ); 209 210profiler .setNextTag ("flashAttn.2" ); 211return mulMat (v ,tmp ); 212 } 213 } 214 215Tensor res = createTensor ( eDataType::FP32 ,q .ne ); 216flashAttention (q ,k ,v ,res ,masked ); 217 218#if 0 219Tensor tmpMat = mulMat (k ,q ); 220float scale = (float )(1.0 /sqrt ( (double )(int )q .ne [0 ] ) ); 221softMax (tmpMat ,scale ); 222Tensor testRes = mulMat (v ,tmpMat ); 223computeDiff (res ,testRes )."flashAttention mulmat" ); 224#endif 225 226return res ; 227} 228 229Tensor MlContext ::copy (const Tensor & a ,eDataType type , std::initializer_list < uint32_t > size ) 230{ 231const size_t dims = size .size (); 232if (0 == dims || dims > 4 ) 233throw E_BOUNDS ; 234 235size_t nRequested = 1 ; 236for (size_t i = 0 ;i < dims ;i ++ ) 237 { 238uint32_t n = size .begin ()[i ]; 239nRequested *=n ; 240 } 241if (nRequested != a .countElements () ) 242throw E_INVALIDARG ; 243 244const eDataType st = a .getType (); 245Tensor res ; 246if (a .isContinuous ()&& st == type ) 247 { 248// Same type, and it's dense - no need to call any compute shaders, equal to reshape 249res = a ; 250for (size_t i = 0 ;i < dims ;i ++ ) 251res .ne [i ]= size .begin ()[i ];; 252for (size_t i = dims ;i < 4 ;i ++ ) 253res .ne [i ]= 1 ; 254res .setDenseStrides (); 255 } 256else 257 { 258// Either converting non-continuous to continuous, or converting types 259res = createTensor (type ,size ); 260copyImpl (a ,res ,st == eDataType::FP32 && type == eDataType::FP16 ); 261 } 262return res ; 263} 264 265void MlContext ::copyInPlace (Tensor & dest ,const Tensor & a ,eDataType type , std::initializer_list < uint32_t > size ) 266{ 267assert (type == dest .getType () ); 268 269const size_t dims = size .size (); 270if (0 == dims || dims > 4 ) 271throw E_BOUNDS ; 272 273size_t nRequested = 1 ; 274for (size_t i = 0 ;i < dims ;i ++ ) 275 { 276uint32_t n = size .begin ()[i ]; 277nRequested *=n ; 278 } 279if (nRequested != a .countElements ()|| nRequested != dest .countElements () ) 280throw E_INVALIDARG ; 281 282// Reshape the destination 283for (size_t i = 0 ;i < dims ;i ++ ) 284dest .ne [i ]= size .begin ()[i ]; 285for (size_t i = dims ;i < 4 ;i ++ ) 286dest .ne [i ]= 1 ; 287dest .setDenseStrides (); 288 289// Call the shader 290const eDataType st = a .getType (); 291copyImpl (a ,dest ,st == eDataType::FP32 && type == eDataType::FP16 ); 292}