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
cacec67
master
1#include "stdafx.h" 2#include "GpuProfiler.h" 3#include "GpuProfilerSimple.h" 4using namespace DirectCompute ; 5 6inline void GpuProfiler ::sProfilerData::reset () 7{ 8_mm_storeu_si128 ( (__m128i * )& callsPending ,_mm_setzero_si128 () ); 9} 10 11inline void GpuProfiler ::sProfilerData::addPending (int64_t time ) 12{ 13callsPending ++ ; 14timePending += time ; 15} 16 17inline void GpuProfiler ::sProfilerData::dropPending () 18{ 19callsPending = 0 ; 20timePending = 0 ; 21} 22 23inline void GpuProfiler ::sProfilerData::makeTime (uint64_t freq ) 24{ 25dest -> count += callsPending ; 26dest -> totalTicks += ::makeTime (timePending ,freq ); 27callsPending = 0 ; 28timePending = 0 ; 29} 30 31HRESULT GpuProfiler ::Queue ::create () 32{ 33ID3D11Device * const dev = device (); 34 35CD3D11_QUERY_DESC desc {D3D11_QUERY_TIMESTAMP }; 36for (Entry & e :queue ) 37 { 38CHECK (dev -> CreateQuery (& desc ,& e .query ) ); 39e .block = nullptr ; 40e .event = eEvent::None ; 41e .shader = EmptyShader ; 42 } 43return S_OK ; 44} 45 46namespace 47{ 48static uint64_t getTimestamp (ID3D11Query * query ,const DelayExecution & delay ) 49 { 50ID3D11DeviceContext * const ctx = context (); 51 52uint64_t res = 0 ; 53while ( true ) 54 { 55const HRESULT hr = ctx -> GetData (query ,& res ,sizeof (uint64_t ),0 ); 56check (hr ); 57if (S_OK == hr ) 58return res ; 59delay .delay (); 60 } 61 } 62 63static D3D11_QUERY_DATA_TIMESTAMP_DISJOINT waitForDisjointData (ID3D11Query * query ) 64 { 65ID3D11DeviceContext * const ctx = context (); 66ctx -> End (query ); 67 68D3D11_QUERY_DATA_TIMESTAMP_DISJOINT res ; 69while ( true ) 70 { 71const HRESULT hr = ctx -> GetData (query ,& res ,sizeof (D3D11_QUERY_DATA_TIMESTAMP_DISJOINT ),0 ); 72check (hr ); 73if (S_OK == hr ) 74return res ; 75Sleep (1 ); 76 } 77 } 78} 79 80void GpuProfiler ::Queue ::Entry ::join (GpuProfiler & owner ) 81{ 82assert (nullptr != block ); 83 84uint64_t res = getTimestamp (query ,owner .delay ); 85#if PROFILER_COLLECT_TAGS 86block -> haveTimestamp (event ,shader ,tag ,res ,owner ); 87#else 88block -> haveTimestamp (event ,shader ,0 ,res ,owner ); 89#endif 90block = nullptr ; 91event = eEvent::None ; 92shader = EmptyShader ; 93} 94 95void GpuProfiler ::Queue ::submit (BlockState * block ,eEvent evt ,uint16_t shader ,uint16_t tag ) 96{ 97// if( evt == GpuProfiler::eEvent::Shader && shader == 0 ) __debugbreak(); 98assert (nullptr != block ); 99 100Entry & e = queue [nextEntry ]; 101if (nullptr != e .block ) 102e .join (owner ); 103 104e .block = block ; 105e .event = evt ; 106e .shader = shader ; 107#if PROFILER_COLLECT_TAGS 108e .tag = tag ; 109#endif 110context ()-> End (e .query ); 111nextEntry = (nextEntry + 1 ) %queueLength ; 112} 113 114void GpuProfiler ::Queue ::join () 115{ 116while ( true ) 117 { 118Entry & e = queue [nextEntry ]; 119if (nullptr == e .block ) 120return ; 121e .join (owner ); 122nextEntry = (nextEntry + 1 ) %queueLength ; 123 } 124} 125 126static inline uint32_t makeTagKey (uint16_t cs ,uint16_t tag ) 127{ 128uint32_t r = cs ; 129r = r <<16 ; 130r |=tag ; 131return r ; 132} 133 134void GpuProfiler ::BlockState ::completePrevShader (uint64_t time ,GpuProfiler & profiler ) 135{ 136if (shaderStart == -1 ) 137return ; 138assert (prevShader != EmptyShader ); 139const int64_t elapsed = (int64_t )time - shaderStart ; 140 141sProfilerData * dest = nullptr ; 142auto * p = profiler .results .Lookup (prevShader ); 143if (nullptr != p ) 144dest = & p -> m_value ; 145else 146 { 147sProfilerData & res = profiler .results [prevShader ]; 148res .dest = & profiler .dest .measure ( (eComputeShader )prevShader ); 149dest = & res ; 150 } 151dest -> addPending (elapsed ); 152 153#if PROFILER_COLLECT_TAGS 154if (0 != prevShaderTag ) 155 { 156const uint32_t key = makeTagKey (prevShader ,prevShaderTag ); 157auto * pt = profiler .resultsTagged .Lookup (key ); 158if (nullptr != pt ) 159dest = & pt -> m_value ; 160else 161 { 162sProfilerData & res = profiler .resultsTagged [key ]; 163res .dest = & profiler .dest .measure ( (eComputeShader )prevShader ,prevShaderTag ); 164dest = & res ; 165 } 166dest -> addPending (elapsed ); 167 } 168#endif 169prevShader = EmptyShader ; 170prevShaderTag = 0 ; 171shaderStart = -1 ; 172} 173 174void GpuProfiler ::BlockState ::haveTimestamp (eEvent evt ,uint16_t cs ,uint16_t tag ,uint64_t time ,GpuProfiler & profiler ) 175{ 176switch (evt ) 177 { 178case eEvent::BlockStart : 179assert (-1 == timeStart ); 180assert (-1 == shaderStart ); 181assert (cs == EmptyShader ); 182timeStart = (int64_t )time ; 183if (nullptr != parentBlock ) 184parentBlock -> completePrevShader (time ,profiler ); 185return ; 186case eEvent::BlockEnd : 187assert (-1 != timeStart ); 188assert (cs == EmptyShader ); 189completePrevShader (time ,profiler ); 190destBlock -> addPending ( (int64_t )time - timeStart ); 191timeStart = -1 ; 192return ; 193case eEvent::Shader : 194assert (cs != EmptyShader ); 195// if( cs == (uint16_t)0 ) __debugbreak(); 196completePrevShader (time ,profiler ); 197prevShader = cs ; 198prevShaderTag = tag ; 199shaderStart = (int64_t )time ; 200return ; 201 } 202assert ( false ); 203} 204 205HRESULT GpuProfiler ::create (size_t maxDepth ) 206{ 207CD3D11_QUERY_DESC desc {D3D11_QUERY_TIMESTAMP_DISJOINT }; 208CHECK (device ()-> CreateQuery (& desc ,& disjoint ) ); 209CHECK (queries .create () ); 210stack .reserve (maxDepth ); 211return S_OK ; 212} 213 214void GpuProfiler ::blockStart (eProfilerBlock which ) 215{ 216BlockState * parentBlock ; 217if (stack .empty () ) 218 { 219context ()-> Begin (disjoint ); 220parentBlock = nullptr ; 221 } 222else 223parentBlock = * stack .rbegin (); 224 225BlockState * bs = nullptr ; 226auto p = blockStates .Lookup (which ); 227if (nullptr != p ) 228bs = & p -> m_value ; 229else 230 { 231BlockState & block = blockStates [which ]; 232block .destBlock = & results [ (uint16_t )which ]; 233block .destBlock -> dest = & dest .measure (which ); 234bs = & block ; 235 } 236bs -> parentBlock = parentBlock ; 237queries .submit (bs , eEvent::BlockStart ); 238stack .push_back (bs ); 239} 240 241void GpuProfiler ::blockEnd () 242{ 243assert ( !stack .empty () ); 244BlockState * const bs = * stack .rbegin (); 245queries .submit (bs , eEvent::BlockEnd ); 246stack .pop_back (); 247 248if ( !stack .empty () ) 249return ; 250 251const D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dtsd = waitForDisjointData (disjoint ); 252queries .join (); 253 254if ( !dtsd .Disjoint ) 255 { 256// Fortunately, these timers appear to be relatively high resolution. 257// Specifically, on the iGPU inside Ryzen 7 5700G that frequency is 1E+8 = 100 MHz 258// On nVidia 1080Ti, that frequency is 1E+9 = 1 GHz 259const uint64_t freq = dtsd .Frequency ; 260resultsMakeTime (freq ); 261 } 262else 263 { 264// Something occurred in between the query's ID3D11DeviceContext::Begin and ID3D11DeviceContext::End calls 265// that caused the timestamp counter to become discontinuous or disjoint, such as unplugging the AC cord on a laptop, overheating, or throttling up/down due to laptop savings events. 266// The timestamp returned by ID3D11DeviceContext::GetData for a timestamp query is only reliable if Disjoint is FALSE. 267resultsDropPending (); 268 } 269} 270 271void GpuProfiler ::computeShader (eComputeShader cs ) 272{ 273assert ( !stack .empty () ); 274if ( !profileShaders ) 275return ; 276 277BlockState * const bs = * stack .rbegin (); 278#if PROFILER_COLLECT_TAGS 279queries .submit (bs , eEvent::Shader , (uint16_t )cs ,m_nextTag ); 280m_nextTag = 0 ; 281#else 282queries .submit (bs , eEvent::Shader , (uint16_t )cs ); 283#endif 284} 285 286void GpuProfiler ::resultsDropPending () 287{ 288for (POSITION pos = results .GetStartPosition ();nullptr != pos ; ) 289results .GetNextValue (pos ).dropPending (); 290#if PROFILER_COLLECT_TAGS 291for (POSITION pos = resultsTagged .GetStartPosition ();nullptr != pos ; ) 292resultsTagged .GetNextValue (pos ).dropPending (); 293#endif 294} 295 296void GpuProfiler ::resultsMakeTime (uint64_t freq ) 297{ 298for (POSITION pos = results .GetStartPosition ();nullptr != pos ; ) 299results .GetNextValue (pos ).makeTime (freq ); 300#if PROFILER_COLLECT_TAGS 301for (POSITION pos = resultsTagged .GetStartPosition ();nullptr != pos ; ) 302resultsTagged .GetNextValue (pos ).makeTime (freq ); 303#endif 304} 305 306void GpuProfiler ::resultsReset () 307{ 308for (POSITION pos = results .GetStartPosition ();nullptr != pos ; ) 309results .GetNextValue (pos ).reset (); 310#if PROFILER_COLLECT_TAGS 311for (POSITION pos = resultsTagged .GetStartPosition ();nullptr != pos ; ) 312resultsTagged .GetNextValue (pos ).reset (); 313#endif 314} 315 316#if PROFILER_COLLECT_TAGS 317uint16_t __declspec(noinline )GpuProfiler ::setNextTag (const char * name ) 318{ 319uint16_t tag = dest .makeTagId (name ); 320m_nextTag = tag ; 321return tag ; 322} 323#endif 324 325HRESULT GpuProfilerSimple ::create () 326{ 327ID3D11Device * const dev = device (); 328 329CD3D11_QUERY_DESC desc {D3D11_QUERY_TIMESTAMP_DISJOINT }; 330CHECK (dev -> CreateQuery (& desc ,& disjoint ) ); 331 332desc .Query = D3D11_QUERY_TIMESTAMP ; 333CHECK (dev -> CreateQuery (& desc ,& begin ) ); 334CHECK (dev -> CreateQuery (& desc ,& end ) ); 335 336context ()-> Begin (disjoint ); 337context ()-> End (begin ); 338return S_OK ; 339} 340 341HRESULT GpuProfilerSimple ::time (uint64_t & rdi )const 342{ 343context ()-> End (end ); 344 345try 346 { 347const D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dtsd = waitForDisjointData (disjoint ); 348const uint64_t t2 = getTimestamp (end ,delay ); 349const uint64_t t1 = getTimestamp (begin ,delay ); 350 351if ( !dtsd .Disjoint ) 352 { 353rdi = makeTime (t2 - t1 ,dtsd .Frequency ); 354return S_OK ; 355 } 356else 357 { 358// Something occurred in between the query's ID3D11DeviceContext::Begin and ID3D11DeviceContext::End calls 359// that caused the timestamp counter to become discontinuous or disjoint, such as unplugging the AC cord on a laptop, overheating, or throttling up/down due to laptop savings events. 360// The timestamp returned by ID3D11DeviceContext::GetData for a timestamp query is only reliable if Disjoint is FALSE. 361rdi = -1 ; 362return S_FALSE ; 363 } 364 } 365catch (HRESULT hr ) 366 { 367return hr ; 368 } 369}