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#include "stdafx.h" 2#include "MlContext.h" 3#include "../D3D/shaderNames.h" 4#include "LookupTables.h" 5#include "../D3D/shaders.h" 6#include "../D3D/Binder.h" 7#include "../D3D/MappedResource.h" 8#include "../D3D/downloadBuffer.h" 9#include "testUtils.h" 10#include "reshapedMultiply.h" 11using namespace DirectCompute ; 12 13// TODO: change this to a field, and set to false when the GPU doesn't support FP64 math 14// Most notably, Intel has dropped the support recently: 15// https://www.intel.com/content/www/us/en/developer/articles/guide/lp-api-developer-optimization-guide.html#inpage-nav-3-8-undefined 16// "To improve power and performance", LOL 17constexpr bool usePreciseComputeShaders = true; 18 19MlContext ::MlContext (Whisper ::ProfileCollection & profileColl ) : 20profiler (profileColl ) 21{ 22check (cb .create () ); 23check (profiler .create () ); 24} 25 26void MlContext ::bindShader (eComputeShader cs ) 27{ 28DirectCompute ::bindShader (cs ); 29profiler .computeShader (cs ); 30} 31 32void MlContext ::mulMatDot (const Tensor & src0 ,const Tensor & src1 ,Tensor & res ) 33{ 34const auto & size1 = src1 .ne ; 35if (1 != size1 [3 ] ) 36throw E_UNEXPECTED ; 37 38const size_t tempLength = size1 [0 ]* size1 [1 ]* size1 [2 ]* size1 [3 ]; 39const TensorGpuViews & tempBuffer = temp .fp16 (tempLength ); 40cb .bind (); 41 42bindShader ( eComputeShader::mulMatDotReshape ); 43cb .update (src1 ); 44Binder bind ; 45bind .bind (src1 ,tempBuffer ); 46context ()-> Dispatch (size1 [1 ],size1 [2 ],1 ); 47 48bindShader ( eComputeShader::mulMatDotMain ); 49cb .update (src0 ,src1 ,res ); 50bind .bind (src0 ,tempBuffer ,res ); 51 52const auto & size0 = src0 .ne ; 53// total rows in src0 54const uint32_t nr = size0 [1 ]* size0 [2 ]* size0 [3 ]; 55context ()-> Dispatch (size1 [1 ],nr ,1 ); 56} 57 58void MlContext ::mulMatMad (const Tensor & a ,const Tensor & b ,Tensor & res ) 59{ 60// CaptureRaii renderDoc; 61const uint32_t resultElts = res .countElements (); 62constexpr uint32_t nth = 4 ; 63 64uint32_t fp16 ; 65TensorGpuViews tempBuffer ; 66 67const eDataType dataType = a .getType (); 68if (dataType == eDataType::FP16 ) 69 { 70fp16 = TRUE; 71tempBuffer = temp .fp16 (resultElts * nth ); 72 } 73else if (dataType == eDataType::FP32 ) 74 { 75fp16 = FALSE; 76tempBuffer = temp .fp32 (resultElts * nth ); 77 } 78else 79throw E_INVALIDARG ; 80 81TensorShape resultShape = res ; 82resultShape .nb = {fp16 ,resultElts ,0 ,0 }; 83 84cb .update (a ,b ,resultShape ); 85bindShader ( eComputeShader::mulMatMadMain ); 86cb .bind (); 87 88Binder bind ; 89bind .bind ( {a ,b }, {res ,tempBuffer } ); 90context ()-> Dispatch (b .ne [1 ],b .ne [2 ],b .ne [3 ] ); 91} 92 93void MlContext ::mulMatTiled (const Tensor & a ,const Tensor & b ,Tensor & res ) 94{ 95cb .update (a ,b ,res ); 96cb .bind (); 97 98Binder bind ; 99bind .bind (a ,b ,res ); 100 101if (b .ne [1 ]== 1 ) 102 { 103if (b .ne [0 ]!= 1 ) 104 { 105#if 0 106static PrintUniqueTensorSizes printSize ("mulMatByRow" ); 107printSize .a ,b ); 108#endif 109// Tensor B is a single row, we have optimized compute shaders for that use case 110// Even 2 of them, tiled and sequential. Select between these two shaders. 111constexpr uint32_t minHeightToTile = 2 ; 112if (a .ne [1 ]< minHeightToTile ) 113 { 114bindShader ( eComputeShader::mulMatByRow ); 115context ()-> Dispatch (a .ne [1 ],a .ne [2 ],a .ne [3 ] ); 116 } 117else 118 { 119bindShader ( eComputeShader::mulMatByRowTiled ); 120constexpr uint32_t TILE_Y = 64 ; 121const uint32_t groupsX = (a .ne [1 ]+ TILE_Y - 1 ) /TILE_Y ; 122context ()-> Dispatch (groupsX ,a .ne [2 ],a .ne [3 ] ); 123 } 124 } 125else 126 { 127// Tensor B is a single element: we have an optimized shader for that as well 128bindShader ( eComputeShader::mulMatByScalar ); 129context ()-> Dispatch (a .ne [2 ],a .ne [3 ],1 ); 130 } 131 } 132else 133 { 134// According to visual studio debugger, when the second argument of this method is a 2D matrix, the first argument is 2D as well. 135// Assuming both arguments are 2D matrices. 136// For optimal VRAM bandwidth utilization, we compute such matrix products in square tiles, a tile is 32x32 elements. 137// Dispatching one thread group for each tile of the output matrix. 138bindShader ( eComputeShader::mulMatTiled ); 139 140// These compute shaders correctly handle partial tiles on the right and bottom edges of the output matrix, that's why rounding up 141constexpr uint32_t TILE_SIZE = 32 ; 142const uint32_t x = (res .ne [0 ]+ TILE_SIZE - 1 ) /TILE_SIZE ; 143const uint32_t y = (res .ne [1 ]+ TILE_SIZE - 1 ) /TILE_SIZE ; 144 145const uint32_t z = res .ne [2 ]* res .ne [3 ]; 146context ()-> Dispatch (x ,y ,z ); 147 } 148} 149 150void MlContext ::mulMat (const Tensor & src0 ,const Tensor & src1 ,Tensor & res ) 151{ 152const uint32_t nb00 = src0 .nb [0 ]; 153const uint32_t nb01 = src0 .nb [1 ]; 154if (nb01 >=nb00 ) 155mulMatDot (src0 ,src1 ,res ); 156else 157mulMatMad (src0 ,src1 ,res ); 158} 159 160namespace 161{ 162// Must match the HLSL in flashAttention.hlsl 163struct sFlashAttentionConstants 164 { 165TensorShape q ,k ,v ,res ; 166BOOL masked ; 167float scale ; 168uint32_t tempBufferStride ; 169uint32_t zzPadding ; 170 }; 171 172struct sFlashAttnDispatchInfo 173 { 174uint32_t tempStride ; 175uint32_t groupsCount ; 176 }; 177 178sFlashAttnDispatchInfo makeFlashAttentionConstants (CComPtr < ID3D11Buffer >& buffer ,const Tensor & q ,const Tensor & k ,const Tensor & v ,Tensor & res ,bool masked ) 179 { 180if (nullptr == buffer ) 181 { 182CD3D11_BUFFER_DESC desc {sizeof (sFlashAttentionConstants ),D3D11_BIND_CONSTANT_BUFFER ,D3D11_USAGE_DYNAMIC ,D3D11_CPU_ACCESS_WRITE }; 183check (device ()-> CreateBuffer (& desc ,nullptr ,& buffer ) ); 184 } 185 186sFlashAttnDispatchInfo result ; 187 188sFlashAttentionConstants cb ; 189cb .q = q ; 190cb .k = k ; 191cb .v = v ; 192cb .res = res ; 193cb .masked = masked ? TRUE : FALSE; 194 195const int neq0 = (int )cb .q .ne [0 ]; 196const int D = neq0 ; 197cb .scale = (float )(1.0 /sqrt ( (double )(int )D ) ); 198 199const uint32_t nek1 = cb .k .ne [1 ]; 200constexpr uint32_t align = 32 /4 ; 201result .tempStride = ( (nek1 + align - 1 ) /align )* align ; 202cb .tempBufferStride = result .tempStride ; 203cb .zzPadding = 0 ; 204result .groupsCount = cb .q .ne [1 ]* cb .q .ne [2 ]* cb .q .ne [3 ]; 205 206MappedResource mapped ; 207check (mapped .map (buffer , false ) ); 208memcpy (mapped .data (),& cb ,sizeof (cb ) ); 209return result ; 210 } 211} 212 213void MlContext ::flashAttention (const Tensor & q ,const Tensor & k ,const Tensor & v ,Tensor & res ,bool masked ) 214{ 215sFlashAttnDispatchInfo di = makeFlashAttentionConstants (flashAttentionConstants ,q ,k ,v ,res ,masked ); 216 217const uint32_t tempLength = di .tempStride * di .groupsCount ; 218const TensorGpuViews & tb = temp .fp32 (tempLength ); 219 220csSetCB (flashAttentionConstants ); 221ID3D11DeviceContext * const ctx = context (); 222 223Binder bind ; 224bind .bind ( {q ,k ,v ,lookupTables .exponent () }, {res ,tb } ); 225 226if constexpr (usePreciseComputeShaders && !enableInexactOptimizations ) 227 { 228bindShader ( eComputeShader::flashAttentionCompat1 ); 229ctx -> Dispatch (di .groupsCount ,1 ,1 ); 230 231bindShader ( eComputeShader::flashAttentionCompat2 ); 232ctx -> Dispatch ( (di .groupsCount + 31 ) /32 ,1 ,1 ); 233 234bindShader ( eComputeShader::flashAttentionCompat3 ); 235ctx -> Dispatch (di .groupsCount ,1 ,1 ); 236 } 237else 238 { 239// This version is not too bad, e.g. maxAbsDiff = 2.7895e-05, avgDiffSquared = 1.61783e-14 240// And probably much faster. 241// But still, it does not deliver bitwise equality with the reference CPU version 242bindShader ( eComputeShader::flashAttention ); 243ctx -> Dispatch (di .groupsCount ,1 ,1 ); 244 } 245} 246 247namespace 248{ 249// Round up the number to be a multiple of 32 250inline uint32_t roundUp32 (uint32_t x ) 251 { 252return (x + 31 )& ( ~31u ); 253 } 254} 255 256void MlContext ::convolutionImpl (const Tensor & a ,const Tensor & b ,Tensor & res ,bool is2 ) 257{ 258const uint32_t ne00 = a .ne [0 ]; 259const uint32_t ne01 = a .ne [1 ]; 260const uint32_t ne02 = a .ne [2 ]; 261 262const uint32_t ne10 = b .ne [0 ]; 263const uint32_t ne11 = b .ne [1 ]; 264 265const uint32_t nb00 = a .nb [0 ]; 266const uint32_t nb01 = a .nb [1 ]; 267const uint32_t nb02 = a .nb [2 ]; 268 269const uint32_t nb10 = b .nb [0 ]; 270const uint32_t nb11 = b .nb [1 ]; 271 272const uint32_t nb1 = res .nb [1 ]; 273 274const uint32_t ew0 = roundUp32 (ne01 ); 275 276const uint32_t nk = ne00 ; 277const uint32_t nh = nk /2 ; 278 279const uint32_t lenTemp1 = ne02 * ew0 * ne00 ; 280const uint32_t lenTemp2 = (ne10 + ne00 )* ew0 ; 281 282const TensorGpuViews & temp1 = temp .fp16 (lenTemp1 , true ); 283const TensorGpuViews & temp2 = temp .fp16_2 (lenTemp2 , true ); 284 285cb .bind (); 286 287bindShader ( eComputeShader::convolutionPrep1 ); 288cb .update (a ); 289Binder bind ; 290bind .bind (a ,temp1 ); 291context ()-> Dispatch (ne01 ,ne02 ,1 ); 292 293bindShader ( eComputeShader::convolutionPrep2 ); 294cb .update (a ,b ); 295bind .bind (b ,temp2 ); 296context ()-> Dispatch (ne11 ,1 ,1 ); 297 298cb .update (a ,b ,res ); 299bind .bind (temp1 ,temp2 ,res ); 300if (is2 ) 301 { 302if constexpr (enableInexactOptimizations ) 303 { 304constexpr uint32_t KERNEL = 3 ; 305constexpr uint32_t TILE_Y = 8 ; 306if (a .ne [0 ]== KERNEL ) 307 { 308const uint32_t x = ( (ne10 /2 )+ TILE_Y - 1 ) /TILE_Y ; 309bindShader ( eComputeShader::convolutionMain2Fixed ); 310context ()-> Dispatch (x ,ne02 ,1 ); 311return ; 312 } 313 } 314bindShader ( eComputeShader::convolutionMain2 ); 315context ()-> Dispatch (ne10 /2 ,ne02 ,1 ); 316 } 317else 318 { 319bindShader ( eComputeShader::convolutionMain ); 320context ()-> Dispatch (ne10 ,ne02 ,1 ); 321 } 322#if 0 323 std::vector < uint16_t > tmp ; 324downloadBuffer (temp1 ,tmp ); 325dbgWriteBinaryFile (L"conv-gpu-arg1.bin" ,tmp .data (),lenTemp1 * 2 ); 326downloadBuffer (temp2 ,tmp ); 327dbgWriteBinaryFile (L"conv-gpu-arg2.bin" ,tmp .data (),lenTemp1 * 2 ); 328res .download (tempVector ); 329dbgWriteBinaryFile (L"conv-gpu-result.bin" ,tempVector .data (),tempVector .size ()* 4 ); 330#endif 331} 332 333void MlContext ::norm (const Tensor & a ,Tensor & res ) 334{ 335const uint32_t ne01 = a .ne [1 ]; 336const uint32_t ne02 = a .ne [2 ]; 337const uint32_t ne03 = a .ne [3 ]; 338 339cb .bind (); 340cb .update (a ,res ); 341Binder bind ; 342bind .bind (a ,res ); 343 344if constexpr (usePreciseComputeShaders && !enableInexactOptimizations ) 345 { 346bindShader ( eComputeShader::normCompat ); 347context ()-> Dispatch ( (ne01 + 31 ) /32 ,ne02 ,ne03 ); 348 } 349else 350 { 351constexpr uint32_t FIXED_ROW_SIZE = 1024 ; 352eComputeShader cs = (a .ne [0 ]== FIXED_ROW_SIZE ) ? eComputeShader::normFixed : eComputeShader::norm ; 353bindShader (cs ); 354context ()-> Dispatch (ne01 ,ne02 ,ne03 ); 355 } 356} 357 358void MlContext ::cwiseBinary (const Tensor & a ,const Tensor & b ,Tensor & res ,eComputeShader cs ) 359{ 360assert (isSameShape (a ,b ) ); 361assert (isSameShape (a ,res ) ); 362 363bindShader (cs ); 364cb .bind (); 365check (cb .update (a ,b ,res ) ); 366Binder bind ; 367bind .bind (a ,b ,res ); 368 369uint32_t rows = a .countRows (); 370context ()-> Dispatch (rows ,1 ,1 ); 371} 372 373Tensor MlContext ::add (const Tensor & a ,const Tensor & b ) 374{ 375return cwiseBinary (a ,b , eComputeShader::add ); 376} 377 378void MlContext ::addInPlace (Tensor & a ,const Tensor & b ) 379{ 380if ( !isSameShape (a ,b ) ) 381throw E_INVALIDARG ; 382assert (a .getType ()== eDataType::FP32 ); 383 384check (cb .update (a ,b ) ); 385bindShader ( eComputeShader::addInPlace ); 386cb .bind (); 387 388Binder bind ; 389bind .bind (b ,a ); 390context ()-> Dispatch (a .ne [1 ],a .ne [2 ],a .ne [3 ] ); 391} 392 393void MlContext ::copyImpl (const Tensor & a ,Tensor & res ,bool downcastFp32 ) 394{ 395assert (res .isContinuous () ); 396const eComputeShader cs = a .isContinuous () ? eComputeShader::copyConvert : eComputeShader::copyTranspose ; 397bindShader (cs ); 398 399cb .bind (); 400// These two shaders don't need shape of the destination because dense, but they wants a boolean flag whether to implement rounding while downcasting 401TensorShape dummyShape ; 402dummyShape .setZero (); 403dummyShape .ne [0 ]= downcastFp32 ? TRUE : FALSE; 404check (cb .update (a ,dummyShape ) ); 405 406Binder bind ; 407bind .bind (a ,res ); 408context ()-> Dispatch (a .ne [1 ],a .ne [2 ],a .ne [3 ] ); 409} 410 411namespace 412{ 413uint32_t bitcast (float val ) 414 { 415__m128 f = _mm_set_ss (val ); 416__m128i i = _mm_castps_si128 (f ); 417return (uint32_t )_mm_cvtsi128_si32 (i ); 418 } 419} 420 421void MlContext ::scale (Tensor & a ,float mul ) 422{ 423if ( !a .isContinuous () ) 424throw E_INVALIDARG ; 425 426bindShader ( eComputeShader::scaleInPlace ); 427cb .bind (); 428TensorShape dummyShape ; 429dummyShape .setZero (); 430dummyShape .ne [0 ]= bitcast (mul ); 431check (cb .update (a ,dummyShape ) ); 432 433Binder bind ; 434bind .bind (a ); 435context ()-> Dispatch (a .countRows (),1 ,1 ); 436} 437 438void MlContext ::addRepeat (Tensor & a ,const Tensor & b ) 439{ 440check (cb .update (a ,b ) ); 441bindShader ( eComputeShader::addRepeat ); 442cb .bind (); 443 444Binder bind ; 445bind .bind (b ,a ); 446context ()-> Dispatch (a .ne [1 ],a .ne [2 ],a .ne [3 ] ); 447} 448 449void MlContext ::addRepeatScale (Tensor & a ,const Tensor & b ,float scale ) 450{ 451#if 0 452addRepeat (a ,b ); 453this -> scale (a ,scale ); 454return ; 455#endif 456 457TensorShape dummyShape ; 458dummyShape .setZero (); 459dummyShape .ne [0 ]= bitcast (scale ); 460check (cb .update (a ,b ,dummyShape ) ); 461bindShader ( eComputeShader::addRepeatScale ); 462cb .bind (); 463 464Binder bind ; 465bind .bind (b ,a ); 466context ()-> Dispatch (a .ne [1 ],a .ne [2 ],a .ne [3 ] ); 467} 468 469void MlContext ::fmaRepeat (Tensor & a ,const Tensor & mul ,const Tensor & add ) 470{ 471eComputeShader cs ; 472if (isSameShapeAndLayout (mul ,add ) ) 473 { 474cs = eComputeShader::fmaRepeat1 ; 475check (cb .update (a ,mul ) ); 476 } 477else 478 { 479cs = eComputeShader::fmaRepeat2 ; 480check (cb .update (a ,mul ,add ) ); 481 } 482 483bindShader (cs ); 484cb .bind (); 485Binder bind ; 486bind .bind (mul ,add ,a ); 487context ()-> Dispatch (a .ne [1 ],a .ne [2 ],a .ne [3 ] ); 488} 489 490void MlContext ::diagMaskInf (Tensor & a ,uint32_t n_past ) 491{ 492if ( !a .isContinuous () ) 493throw E_INVALIDARG ; 494 495bindShader ( eComputeShader::diagMaskInf ); 496TensorShape dummyShape ; 497dummyShape .setZero (); 498dummyShape .ne [0 ]= n_past ; 499 500cb .bind (); 501check (cb .update (a ,dummyShape ) ); 502 503Binder bind ; 504bind .bind (a ); 505 506const uint32_t n = a .countRows (); 507const uint32_t nr = a .ne [1 ]; 508const uint32_t nz = n /nr ; 509context ()-> Dispatch (nr ,nz ,1 ); 510} 511 512void MlContext ::softMax (Tensor & a ,float inputScale ) 513{ 514if ( !a .isContinuous () ) 515throw E_INVALIDARG ; 516 517if constexpr (usePreciseComputeShaders && !enableInexactOptimizations ) 518 { 519assert (inputScale == 1.0f ); 520bindShader ( eComputeShader::softMaxCompat ); 521const uint32_t nr = a .countRows (); 522TensorShape dummyShape ; 523dummyShape .setZero (); 524dummyShape .ne [0 ]= nr ; 525 526cb .bind (); 527check (cb .update (a ,dummyShape ) ); 528 529Binder bind ; 530bind .bind (lookupTables .exponent (),a ); 531context ()-> Dispatch ( (nr + 31 ) /32 ,1 ,1 ); 532 } 533else 534 { 535#if 0 536static PrintUniqueTensorSizes printSizes ("softMax" ); 537printSizes .a ); 538#endif 539constexpr uint32_t FIXED_ROW_SIZE = 1500 ; 540 541eComputeShader cs ; 542if (a .ne [0 ]== FIXED_ROW_SIZE ) 543cs = eComputeShader::softMaxFixed ; 544else if (a .ne [0 ] >= (1024 * 4 ) ) 545cs = eComputeShader::softMaxLong ; 546else 547cs = eComputeShader::softMax ; 548 549bindShader (cs ); 550const uint32_t nr = a .countRows (); 551TensorShape dummyShape ; 552dummyShape .setZero (); 553dummyShape .ne [0 ]= nr ; 554dummyShape .ne [1 ]= bitcast (inputScale ); 555 556cb .bind (); 557check (cb .update (a ,dummyShape ) ); 558 559Binder bind ; 560bind .bind (lookupTables .exponent (),a ); 561context ()-> Dispatch (nr ,1 ,1 ); 562 } 563} 564 565void MlContext ::addRepeatGelu (Tensor & a ,const Tensor & b ) 566{ 567check (cb .update (a ,b ) ); 568bindShader ( eComputeShader::addRepeatGelu ); 569cb .bind (); 570 571Binder bind ; 572bind .bind (b ,lookupTables .gelu (),a ); 573context ()-> Dispatch (a .ne [1 ],a .ne [2 ],a .ne [3 ] ); 574} 575 576namespace 577{ 578inline bool canAddRows (const Tensor & tokenEmbedding ,const Tensor & positionalEmbedding ,const Tensor & embd ,uint32_t pastTokensCount ) 579 { 580if (tokenEmbedding .ne [0 ]!= positionalEmbedding .ne [0 ] ) 581return false;// Different row lengths 582if (embd .ne [0 ]+ pastTokensCount > positionalEmbedding .ne [1 ] ) 583return false;// Too many rows requested, positionalEmbedding matrix doesn't have that many 584return true; 585 } 586} 587 588Tensor MlContext ::addRows (const Tensor & tokenEmbedding ,const Tensor & positionalEmbedding ,const Tensor & embd ,uint32_t pastTokensCount ) 589{ 590if ( !canAddRows (tokenEmbedding ,positionalEmbedding ,embd ,pastTokensCount ) ) 591throw E_INVALIDARG ; 592 593const uint32_t rowLength = tokenEmbedding .ne [0 ]; 594const uint32_t rows = embd .ne [0 ]; 595Tensor result = createTensor ( eDataType::FP32 , {rowLength ,rows } ); 596 597TensorShape constants ; 598// rowLength 599constants .ne [0 ]= rowLength ; 600// pastTokensCount 601constants .ne [1 ]= pastTokensCount ; 602// outputRowStride 603constants .ne [2 ]= result .nb [1 ]; 604// embStrides 605constants .nb [0 ]= tokenEmbedding .nb [0 ]; 606constants .nb [1 ]= tokenEmbedding .nb [1 ]; 607// posStrides 608constants .nb [2 ]= positionalEmbedding .nb [0 ]; 609constants .nb [3 ]= positionalEmbedding .nb [1 ]; 610check (cb .update (constants ) ); 611 612bindShader ( eComputeShader::addRows ); 613cb .bind (); 614Binder bind ; 615bind .bind ( {tokenEmbedding ,positionalEmbedding ,embd }, {result } ); 616context ()-> Dispatch (rows ,1 ,1 ); 617return result ; 618} 619 620Tensor MlContext ::reshapePanels (const Tensor & a ) 621{ 622constexpr uint32_t TILE_SIZE = ReshapedMultiply ::TILE_SIZE ; 623 624const eDataType dataType = a .getType (); 625// Reshaping into column major horizontal panels, height = TILE_SIZE, width = width of the source matrix 626 627// Round height to multiple of tile size 628 std::array < uint32_t ,4 > ne = a .ne ; 629// Dispatch a group of threads thread per panel 630const uint32_t groupsX = (ne [1 ]+ TILE_SIZE - 1 ) /TILE_SIZE ; 631ne [1 ]= groupsX * TILE_SIZE ;; 632// Each panel has [ size.x, TILE_SIZE ] elements 633const uint32_t panelSize = ne [0 ]* TILE_SIZE ; 634 635Tensor result = createTensor (dataType ,ne ); 636 637TensorShape constants ; 638constants .setZero (); 639// uint panelSize : packoffset( c2.y ); 640constants .ne [1 ]= panelSize ; 641// uint2 layerStrides: packoffset( c2.z ); 642constants .ne [2 ]= result .nb [2 ]; 643constants .ne [3 ]= result .nb [3 ]; 644 645check (cb .update (a ,constants ) ); 646bindShader ( eComputeShader::matReshapePanels ); 647cb .bind (); 648 649Binder bind ; 650bind .bind (a ,result ); 651context ()-> Dispatch (groupsX ,a .ne [2 ],a .ne [3 ] ); 652 653#if 0 654if (dataType == eDataType::FP32 ) 655 { 656 std::vector < float > v1 ,v2 ; 657a .download (v1 ); 658result .download (v2 ); 659__debugbreak (); 660 } 661else if (dataType == eDataType::FP16 ) 662 { 663 std::vector < uint16_t > v1 ,v2 ; 664a .download (v1 ); 665result .download (v2 ); 666__debugbreak (); 667 } 668#endif 669 670// Set up size and stride expected by the mulMatTiledEx compute shader 671result .ne = a .ne ; 672result .nb [0 ]= 0 ; 673result .nb [1 ]= panelSize ; 674return result ; 675} 676 677Tensor MlContext ::mulMatTiledEx (const Tensor & a ,const Tensor & b ) 678{ 679constexpr uint32_t TILE_SIZE = ReshapedMultiply ::TILE_SIZE ; 680 681if ( !canMulMat (a ,b ) ) 682throw E_INVALIDARG ;// Wrong size 683if (0 != (a .nb [0 ] |b .nb [0 ] ) ) 684throw E_INVALIDARG ;// Both tensors are expected to be pre-transposed into these panels 685 686Tensor res = createTensor ( eDataType::FP32 , {a .ne [1 ],b .ne [1 ],a .ne [2 ],b .ne [3 ] } ); 687 688check (cb .update (a ,b ,res ) ); 689bindShader ( eComputeShader::mulMatTiledEx ); 690cb .bind (); 691 692Binder bind ; 693bind .bind (a ,b ,res ); 694 695const uint32_t x = (res .ne [0 ]+ TILE_SIZE - 1 ) /TILE_SIZE ; 696const uint32_t y = (res .ne [1 ]+ TILE_SIZE - 1 ) /TILE_SIZE ; 697const uint32_t z = res .ne [2 ]* res .ne [3 ]; 698context ()-> Dispatch (x ,y ,z ); 699 700return res ; 701} 702 703Tensor MlContext ::mulMatByRowTiledEx (const Tensor & a ,const Tensor & b ) 704{ 705constexpr uint32_t TILE_SIZE = ReshapedMultiply ::TILE_SIZE ; 706assert (canMulMat (a ,b ) ); 707assert (b .ne [1 ]== 1 ); 708 709Tensor res = createTensor ( eDataType::FP32 , {a .ne [1 ],1 ,a .ne [2 ],b .ne [3 ] } ); 710 711check (cb .update (a ,b ,res ) ); 712bindShader ( eComputeShader::mulMatByRowTiledEx ); 713cb .bind (); 714 715Binder bind ; 716bind .bind (a ,b ,res ); 717 718const uint32_t x = (res .ne [0 ]+ TILE_SIZE - 1 ) /TILE_SIZE ; 719const uint32_t y = res .ne [2 ]; 720const uint32_t z = res .ne [3 ]; 721context ()-> Dispatch (x ,y ,z ); 722 723return res ; 724} 725 726void MlContext ::addRepeatEx (Tensor & dest ,const Tensor & pattern ,const Tensor & finalAdd ) 727{ 728if ( !isSameShape (dest ,finalAdd ) ) 729throw E_INVALIDARG ; 730assert (dest .getType ()== eDataType::FP32 ); 731 732check (cb .update (dest ,pattern ,finalAdd ) ); 733bindShader ( eComputeShader::addRepeatEx ); 734cb .bind (); 735 736Binder bind ; 737bind .bind (pattern ,finalAdd ,dest ); 738context ()-> Dispatch (dest .ne [1 ],dest .ne [2 ],dest .ne [3 ] ); 739} 740 741__m128i MlContext ::getMemoryUse ()const 742{ 743__m128i v = cb .getMemoryUse (); 744v = _mm_add_epi64 (v ,temp .getMemoryUse () ); 745v = _mm_add_epi64 (v ,bufferMemoryUsage (flashAttentionConstants ) ); 746v = _mm_add_epi64 (v ,lookupTables .getMemoryUsage () ); 747return v ; 748}