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

KonstantinExperimental, alternative busy wait implementationcacec67

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