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

KonstantinBugfix: when processing files, “Run” CPU block was erroneously measured twiceaaeab77

master
7.6 KiB334 linesraw
1#include "stdafx.h"
2#include "ProfileCollection.h"
3#include "GpuProfiler.h"
4#include "../Whisper/WhisperModel.h"
5#include "../D3D/shaderNames.h"
6using namespace Whisper;
7
8ProfileCollection::Measure& ProfileCollection::measure( DirectCompute::eProfilerBlock which )
9{
10	uint32_t key = (uint16_t)which;
11	key |= 0x20000;
12	return measures[ key ];
13}
14
15ProfileCollection::Measure& ProfileCollection::measure( DirectCompute::eComputeShader which )
16{
17	uint32_t key = (uint16_t)which;
18	key |= 0x30000;
19	return measures[ key ];
20}
21
22ProfileCollection::Measure& ProfileCollection::measure( eCpuBlock which )
23{
24	uint32_t key = (uint8_t)which;
25	key |= 0x10000;
26	CComCritSecLock<CComAutoCriticalSection> lock{ critSec };
27	return measures[ key ];
28}
29
30#if PROFILER_COLLECT_TAGS
31ProfileCollection::Measure& ProfileCollection::measure( DirectCompute::eComputeShader which, uint16_t tag )
32{
33	uint32_t key = (uint8_t)which;
34	key = key << 16;
35	key |= tag;
36	CComCritSecLock<CComAutoCriticalSection> lock{ critSec };
37	return taggedShaders[ key ];
38}
39#endif
40
41namespace
42{
43	using pfnPrintEnum = const char* ( * )( uint16_t val );
44
45	static const char* printCpuBlock( uint16_t id )
46	{
47		const eCpuBlock which = (eCpuBlock)id;
48		switch( which )
49		{
50#define V(x) case eCpuBlock::x: return #x
51			V( LoadModel );
52			V( RunComplete );
53			V( Run );
54			V( Callbacks );
55			V( Spectrogram );
56			V( Sample );
57			V( VAD );
58			V( Encode );
59			V( Decode );
60			V( DecodeStep );
61			V( DecodeLayer );
62#undef V
63		}
64		assert( false );
65		return nullptr;
66	}
67
68	static const char* printGpuBlock( uint16_t id )
69	{
70		using DirectCompute::eProfilerBlock;
71		const eProfilerBlock which = (eProfilerBlock)id;
72
73		switch( which )
74		{
75#define V(x) case eProfilerBlock::x: return #x
76			V( LoadModel );
77			V( Run );
78			V( Encode );
79			V( EncodeLayer );
80			V( Decode );
81			V( DecodeStep );
82			V( DecodeLayer );
83#undef V
84		}
85		assert( false );
86		return nullptr;
87	}
88
89	static const char* printShader( uint16_t id )
90	{
91		return DirectCompute::computeShaderName( (DirectCompute::eComputeShader)id );
92	}
93
94	static pfnPrintEnum printSectionStart( uint16_t type )
95	{
96		switch( type )
97		{
98		case 1:
99			logInfo( u8"    CPU Tasks" );
100			return &printCpuBlock;
101		case 2:
102			logInfo( u8"    GPU Tasks" );
103			return &printGpuBlock;
104		case 3:
105			logInfo( u8"    Compute Shaders" );
106			return &printShader;
107		default:
108			return nullptr;
109		}
110	}
111
112	struct PrintedTime
113	{
114		double value;
115		const char* unit;
116
117		PrintedTime( uint64_t ticks )
118		{
119			const double dbl = (double)(int64_t)ticks;
120			if( ticks >= 10'000'000 )
121			{
122				value = dbl / 1.0E+7;
123				unit = "seconds";
124			}
125			else if( ticks >= 10'000 )
126			{
127				value = dbl / 1.0E+4;
128				unit = "milliseconds";
129			}
130			else
131			{
132				value = dbl / 1.0E+1;
133				unit = "microseconds";
134			}
135		}
136		PrintedTime( double dbl )
137		{
138			if( dbl >= 10'000'000 )
139			{
140				value = dbl / 1.0E+7;
141				unit = "seconds";
142			}
143			else if( dbl >= 10'000 )
144			{
145				value = dbl / 1.0E+4;
146				unit = "milliseconds";
147			}
148			else
149			{
150				value = dbl / 1.0E+1;
151				unit = "microseconds";
152			}
153		}
154	};
155}
156
157void ProfileCollection::Measure::print( const char* name ) const
158{
159	PrintedTime total{ totalTicks };
160	if( 1 == count )
161		logInfo( u8"%s\t%g %s", name, total.value, total.unit );
162	else
163	{
164		PrintedTime avg = (double)totalTicks / (double)(int64_t)count;
165		logInfo( u8"%s\t%g %s, %zu calls, %g %s average", name, total.value, total.unit, count, avg.value, avg.unit );
166	}
167}
168
169#if PROFILER_COLLECT_TAGS
170struct TaggedShaderCmp
171{
172	bool operator()( uint16_t cs, uint32_t key ) const
173	{
174		return cs < key >> 16;
175	}
176	bool operator()( uint32_t key, uint16_t cs ) const
177	{
178		return key >> 16 < cs;
179	}
180};
181
182void ProfileCollection::TaggedTemp::print() const
183{
184	PrintedTime total{ ticks };
185	if( 1 == count )
186		logInfo( u8"  %s\t%g %s", name, total.value, total.unit );
187	else
188	{
189		PrintedTime avg = (double)ticks / (double)(int64_t)count;
190		logInfo( u8"  %s\t%g %s, %zu calls, %g %s average", name, total.value, total.unit, count, avg.value, avg.unit );
191	}
192}
193#endif
194
195void ProfileCollection::print()
196{
197	keysTemp.clear();
198	for( POSITION pos = measures.GetStartPosition(); nullptr != pos; )
199	{
200		auto* p = measures.GetNext( pos );
201		if( p->m_value.count == 0 )
202			continue;
203		keysTemp.push_back( p->m_key );
204	}
205
206	std::sort( keysTemp.begin(), keysTemp.end() );
207	auto it = std::lower_bound( keysTemp.begin(), keysTemp.end(), 0x30000u );
208	if( it != keysTemp.end() )
209	{
210		auto lambda = [ this ]( uint32_t a, uint32_t b )
211		{
212			const uint64_t ta = measures.Lookup( a )->m_value.totalTicks;
213			const uint64_t tb = measures.Lookup( b )->m_value.totalTicks;
214			return ta > tb;
215		};
216		std::stable_sort( it, keysTemp.end(), lambda );
217	}
218
219#if PROFILER_COLLECT_TAGS
220	taggedKeysTemp.clear();
221	for( POSITION pos = taggedShaders.GetStartPosition(); nullptr != pos; )
222	{
223		auto* p = taggedShaders.GetNext( pos );
224		if( p->m_value.count == 0 )
225			continue;
226		taggedKeysTemp.push_back( p->m_key );
227	}
228	std::sort( taggedKeysTemp.begin(), taggedKeysTemp.end() );
229#endif
230
231	uint16_t prevKeyType = 0;
232	pfnPrintEnum pfn = nullptr;
233	for( uint32_t k : keysTemp )
234	{
235		const uint16_t type = (uint16_t)( k >> 16 );
236		if( type != prevKeyType )
237		{
238			prevKeyType = type;
239			pfn = printSectionStart( type );
240		}
241		if( pfn == nullptr )
242			continue;
243		const auto* p = measures.Lookup( k );
244		assert( nullptr != p );
245		p->m_value.print( pfn( (uint16_t)k ) );
246
247#if PROFILER_COLLECT_TAGS
248		if( type == 3 )	
249		{
250			// Compute shader
251			auto range = std::equal_range( taggedKeysTemp.begin(), taggedKeysTemp.end(), (uint16_t)k, TaggedShaderCmp{} );
252			if( range.first != range.second )
253			{
254				// We have at least 1 tag for that compute shader
255				taggedTimes.clear();
256				uint64_t totalTicks = 0;
257				size_t totalCount = 0;
258				for( auto it = range.first; it != range.second; it++ )
259				{
260					const uint32_t key = *it;
261					const uint16_t tagId = (uint16_t)key;
262					assert( 0 != tagId );
263					const auto* p = taggedShaders.Lookup( key );
264					assert( nullptr != p );
265
266					auto& rdi = taggedTimes.emplace_back();
267					rdi.ticks = p->m_value.totalTicks;
268					totalTicks += p->m_value.totalTicks;
269
270					rdi.count = p->m_value.count;
271					totalCount += p->m_value.count;
272
273					rdi.name = tagNames[ tagId ];
274				}
275
276				assert( totalCount <= p->m_value.count );
277				if( totalCount < p->m_value.count )
278				{
279					auto& rdi = taggedTimes.emplace_back();
280					rdi.ticks = p->m_value.totalTicks - totalTicks;
281					rdi.count = p->m_value.count - totalCount;
282					rdi.name = tagNames[ 0 ];
283				}
284				std::stable_sort( taggedTimes.begin(), taggedTimes.end() );
285				for( const auto& e : taggedTimes )
286					e.print();
287			}
288		}
289#endif
290	}
291}
292
293void ProfileCollection::reset()
294{
295	for( POSITION pos = measures.GetStartPosition(); nullptr != pos; )
296		measures.GetNextValue( pos ).reset();
297}
298
299ProfileCollection::ProfileCollection( const WhisperModel& model )
300{
301	const __m128i vals = model.getLoadTimes();
302
303	uint64_t s = (uint64_t)_mm_cvtsi128_si64( vals );
304	measure( eCpuBlock::LoadModel ).add( s );
305
306	s = (uint64_t)_mm_extract_epi64( vals, 1 );
307	measure( DirectCompute::eProfilerBlock::LoadModel ).add( s );
308#if PROFILER_COLLECT_TAGS
309	// Tag ID 0 means no tag at all. makeTagId() method returns 0 for nullptr name, and starts numbering with 1 for non-empoty tag names
310	// Push the tag name corresponding to ID = 0, this way we can index directly with tag IDs.
311	tagNames.push_back( "<untagged>" );
312#endif
313}
314
315uint16_t ProfileCollection::makeTagId( const char* tag )
316{
317#if PROFILER_COLLECT_TAGS
318	if( nullptr == tag )
319		return 0;
320	auto p = tagIDs.Lookup( tag );
321	if( nullptr != p )
322		return p->m_value;
323	const size_t newTag = tagIDs.GetCount() + 1;
324	if( newTag <= 0xFFFF )
325	{
326		tagIDs.SetAt( tag, (uint16_t)newTag );
327		tagNames.push_back( tag );
328		return (uint16_t)newTag;
329	}
330	throw DISP_E_OVERFLOW;
331#else
332	return 0;
333#endif
334}