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

KonstantinSource codes8c4603c

master
9.0 KiB401 linesraw
1#include <stdafx.h>
2#include <atomic>
3#include "Tensor.h"
4using namespace CpuCompute;
5
6#if TENSOR_INTERNAL_ALLOC
7namespace
8{
9	// This structure is immediately before the payload of every tensor which has an internally-allocated memory buffer
10	class alignas( 32 ) sTensorMemoryHeader
11	{
12		std::atomic_ptrdiff_t refCounter;
13	public:
14		// Reset the counter to the specified value
15		void reset( ptrdiff_t rc )
16		{
17			refCounter = rc;
18		}
19		// Increment the ref.counter
20		void increment()
21		{
22			refCounter++;
23		}
24		// Decrement the ref.counter, and return true if it reached zero as the result
25		bool decrement()
26		{
27			ptrdiff_t val = --refCounter;
28			assert( val >= 0 );
29			return 0 == val;
30		}
31	};
32
33	inline sTensorMemoryHeader* getMemBlockHeader( void* pv )
34	{
35		assert( nullptr != pv );
36		uint8_t* pb = (uint8_t*)pv;
37		static_assert( sizeof( sTensorMemoryHeader ) == 32 );
38		return (sTensorMemoryHeader*)( pb - sizeof( sTensorMemoryHeader ) );
39	}
40
41	inline void releaseBlock( sTensorMemoryHeader* pointer )
42	{
43		assert( nullptr != pointer );
44		_aligned_free( pointer );
45	}
46
47	inline void* allocateBlock( size_t cb, ptrdiff_t initialRefCounter = 1 )
48	{
49		cb += sizeof( sTensorMemoryHeader );
50		void* pv = _aligned_malloc( cb, 32 );
51		if( nullptr == pv )
52			return nullptr;
53
54		sTensorMemoryHeader* header = (sTensorMemoryHeader*)pv;
55		header->reset( initialRefCounter );
56		return ( (uint8_t*)pv ) + sizeof( sTensorMemoryHeader );
57	}
58}
59
60void Tensor::deallocate()
61{
62	if( ownsMemory && nullptr != m_data )
63	{
64		sTensorMemoryHeader* const header = getMemBlockHeader( m_data );
65		if( header->decrement() )
66		{
67			// This tensor is the last one which had a reference to that block of memory
68			// Release the memory back to the heap
69			releaseBlock( header );
70		}
71	}
72	ownsMemory = false;
73
74	TensorShape::setZero();
75	m_data = nullptr;
76	m_type = (eDataType)0xFF;
77}
78#endif
79
80Tensor::Tensor( const Tensor& that )
81{
82	store( ne, that.sizeVec() );
83	store( nb, that.stridesVec() );
84	m_data = that.m_data;
85	m_type = that.m_type;
86#if TENSOR_INTERNAL_ALLOC
87	if( that.ownsMemory && nullptr != m_data )
88	{
89		getMemBlockHeader( m_data )->increment();
90		ownsMemory = true;
91	}
92	else
93		ownsMemory = false;
94#endif
95}
96
97Tensor::Tensor( Tensor&& that ) noexcept
98{
99	store( ne, that.sizeVec() );
100	store( nb, that.stridesVec() );
101	m_data = that.m_data;
102	m_type = that.m_type;
103#if TENSOR_INTERNAL_ALLOC
104	ownsMemory = that.ownsMemory;
105	that.ownsMemory = false;
106#endif
107	that.m_data = nullptr;
108}
109
110void Tensor::operator=( const Tensor& that )
111{
112	assert( this != &that );
113#if TENSOR_INTERNAL_ALLOC
114	deallocate();
115#endif
116
117	store( ne, that.sizeVec() );
118	store( nb, that.stridesVec() );
119	m_data = that.m_data;
120	m_type = that.m_type;
121#if TENSOR_INTERNAL_ALLOC
122	if( that.ownsMemory && nullptr != m_data )
123	{
124		getMemBlockHeader( m_data )->increment();
125		ownsMemory = true;
126	}
127	else
128		ownsMemory = false;
129#endif
130}
131
132void Tensor::operator=( Tensor&& that ) noexcept
133{
134	assert( this != &that );
135#if TENSOR_INTERNAL_ALLOC
136	deallocate();
137#endif
138	store( ne, that.sizeVec() );
139	store( nb, that.stridesVec() );
140	m_data = that.m_data;
141	m_type = that.m_type;
142	that.m_data = nullptr;
143#if TENSOR_INTERNAL_ALLOC
144	ownsMemory = that.ownsMemory;
145	that.ownsMemory = false;
146#endif
147}
148
149HRESULT Tensor::create( eDataType type, const std::array<uint32_t, 4>& sizeElements, iMemoryAllocator* alloc )
150{
151	const size_t len = (size_t)sizeElements[ 0 ] * sizeElements[ 1 ] * sizeElements[ 2 ] * sizeElements[ 3 ];
152	const size_t cbElement = DirectCompute::elementSize( type );
153	const size_t cb = len * cbElement;
154
155#if TENSOR_INTERNAL_ALLOC
156	deallocate();
157#endif
158
159	store( ne, load( sizeElements ) );
160	TensorShape::setDenseStrides();
161	this->m_type = type;
162
163	if( nullptr != alloc )
164	{
165#if TENSOR_INTERNAL_ALLOC
166		ownsMemory = false;
167#endif
168		m_data = alloc->allocate( cb, 32 );
169		if( nullptr == m_data )
170			return E_OUTOFMEMORY;
171		return S_OK;
172	}
173	else
174	{
175#if TENSOR_INTERNAL_ALLOC
176		m_data = allocateBlock( cb, 1 );
177		if( nullptr == m_data )
178			return E_OUTOFMEMORY;
179		ownsMemory = true;
180		return S_OK;
181#else
182		return E_POINTER;
183#endif
184	}
185}
186
187namespace
188{
189	static HRESULT arrayFromList( std::array<uint32_t, 4>& arr, std::initializer_list<uint32_t> list )
190	{
191		const size_t dims = list.size();
192		if( dims == 0 || dims > 4 )
193			return E_INVALIDARG;
194
195		for( size_t i = 0; i < dims; i++ )
196		{
197			uint32_t u = list.begin()[ i ];
198			if( u == 0 )
199				return E_INVALIDARG;
200			arr[ i ] = u;
201		}
202
203		for( size_t i = dims; i < 4; i++ )
204			arr[ i ] = 1;
205
206		return S_OK;
207	}
208}
209
210HRESULT Tensor::create( eDataType type, std::initializer_list<uint32_t> sizeElements, iMemoryAllocator* alloc )
211{
212	std::array<uint32_t, 4> arr;
213	CHECK( arrayFromList( arr, sizeElements ) );
214
215	return create( type, arr, alloc );
216}
217
218Tensor::Tensor( void* pointer, eDataType type, std::initializer_list<uint32_t> size )
219{
220	if( nullptr == pointer )
221		throw E_POINTER;
222	check( arrayFromList( ne, size ) );
223	TensorShape::setDenseStrides();
224	m_data = pointer;
225	m_type = type;
226#if TENSOR_INTERNAL_ALLOC
227	ownsMemory = false;
228#endif
229}
230
231Tensor::Tensor( void* pointer, eDataType type, uint32_t length ) noexcept
232{
233	// size = [ length, 1, 1, 1 ]
234	const __m128i one = _mm_set1_epi32( 1 );
235	__m128i v = _mm_insert_epi32( one, (int)length, 0 );
236	store( ne, v );
237	// stride = [ 1, length, length, length ]
238	v = _mm_shuffle_epi32( v, _MM_SHUFFLE( 0, 0, 0, 1 ) );
239	store( nb, v );
240
241	m_data = pointer;
242	m_type = type;
243#if TENSOR_INTERNAL_ALLOC
244	ownsMemory = false;
245#endif
246}
247
248Tensor Tensor::fromData( void* pointer, eDataType type, uint32_t length )
249{
250	HRESULT hr = E_UNEXPECTED;
251	if( nullptr != pointer )
252	{
253		if( 0 != length )
254			return Tensor{ pointer, type, length };
255		else
256			hr = E_INVALIDARG;
257	}
258	else
259		hr = E_POINTER;
260	throw hr;
261}
262
263HRESULT Tensor::attach( void* pointer, eDataType type, std::initializer_list<uint32_t> sizeElements )
264{
265	if( nullptr == pointer )
266		return E_POINTER;
267
268	std::array<uint32_t, 4> arr;
269	CHECK( arrayFromList( arr, sizeElements ) );
270
271#if TENSOR_INTERNAL_ALLOC
272	deallocate();
273#endif
274	store( ne, load( arr ) );
275	TensorShape::setDenseStrides();
276
277	m_data = pointer;
278	this->m_type = type;
279#if TENSOR_INTERNAL_ALLOC
280	ownsMemory = false;
281#endif
282	return S_OK;
283}
284
285Tensor Tensor::reshape3d( uint32_t ne0, uint32_t ne1, uint32_t ne2 ) const
286{
287	if( !isContinuous() )
288		throw E_NOTIMPL;
289	if( countElements() != ne0 * ne1 * ne2 )
290		throw E_INVALIDARG;
291
292	Tensor res = *this;
293	res.ne = { ne0, ne1, ne2, 1 };
294	res.setDenseStrides();
295	return res;
296}
297
298#if TENSOR_GGML_COMPAT
299static const __m128i s_maskAlignment16 = _mm_set1_epi64x( 1 );
300static const __m128i s_maskAlignment32 = _mm_set1_epi64x( 3 );
301
302bool isAlignedProperly( __m128i r0, __m128i r1, __m128i mask )
303{
304	__m128i test = _mm_or_si128( r0, r1 );
305	return (bool)_mm_testz_si128( test, mask );
306}
307
308Tensor::Tensor( const ggml_tensor* ggml )
309{
310	store( ne, load16( ggml->ne ) );
311
312	__m128i r0 = load16( (const int*)&ggml->nb[ 0 ] );
313	__m128i r1 = load16( (const int*)&ggml->nb[ 2 ] );
314	// Divide from bytes into elements by right-shifting the 64-bit integers in these vectors
315	switch( ggml->type )
316	{
317	case GGML_TYPE_F16:
318		assert( isAlignedProperly( r0, r1, s_maskAlignment16 ) );
319		r0 = _mm_srli_epi64( r0, 1 );
320		r1 = _mm_srli_epi64( r1, 1 );
321		m_type = eDataType::FP16;
322		break;
323
324	case GGML_TYPE_F32:
325		assert( isAlignedProperly( r0, r1, s_maskAlignment32 ) );
326		r0 = _mm_srli_epi64( r0, 2 );
327		r1 = _mm_srli_epi64( r1, 2 );
328		m_type = eDataType::FP32;
329		break;
330
331	case GGML_TYPE_I32:
332		assert( isAlignedProperly( r0, r1, s_maskAlignment32 ) );
333		r0 = _mm_srli_epi64( r0, 2 );
334		r1 = _mm_srli_epi64( r1, 2 );
335		m_type = eDataType::U32;
336		break;
337
338	default:
339		throw E_INVALIDARG;
340	}
341	// downcast uint64_t into uint32_t in a single vector
342	r0 = _mm_shuffle_epi32( r0, _MM_SHUFFLE( 3, 3, 2, 0 ) );
343	r1 = _mm_shuffle_epi32( r1, _MM_SHUFFLE( 2, 0, 3, 3 ) );
344	store( nb, _mm_blend_epi16( r0, r1, 0b11110000 ) );
345
346	m_data = ggml->data;
347}
348
349ggml_tensor Tensor::ggml() const
350{
351	ggml_tensor res;
352	memset( &res, 0, sizeof( ggml_tensor ) );
353
354	const __m128i size = sizeVec();
355	store16( res.ne, size );
356
357	const __m128i one = _mm_set1_epi32( 1 );
358	const uint32_t maskOnes = (uint32_t)_mm_movemask_ps( _mm_castsi128_ps( _mm_cmpeq_epi32( size, one ) ) );
359	const uint32_t maskNotOnes = maskOnes ^ 0b1111;
360	unsigned long idx;
361	if( _BitScanReverse( &idx, maskNotOnes ) )
362		res.n_dims = (int)idx + 1;
363	else
364		res.n_dims = 0;
365
366	const __m128i strides = stridesVec();
367	// Upcast strides from u32 to u64
368	const __m128i zero = _mm_setzero_si128();
369	__m128i r0 = _mm_unpacklo_epi32( strides, zero );
370	__m128i r1 = _mm_unpackhi_epi32( strides, zero );
371	// Scale from elements into bytes with left shift vector instructions
372	switch( m_type )
373	{
374	case eDataType::FP16:
375		r0 = _mm_slli_epi64( r0, 1 );
376		r1 = _mm_slli_epi64( r1, 1 );
377		res.type = GGML_TYPE_F16;
378		break;
379	case eDataType::FP32:
380		r0 = _mm_slli_epi64( r0, 2 );
381		r1 = _mm_slli_epi64( r1, 2 );
382		res.type = GGML_TYPE_F32;
383		break;
384	case eDataType::U32:
385		r0 = _mm_slli_epi64( r0, 2 );
386		r1 = _mm_slli_epi64( r1, 2 );
387		res.type = GGML_TYPE_I32;
388		break;
389	default:
390		throw OLE_E_BLANK;
391	}
392
393	store16( &res.nb[ 0 ], r0 );
394	store16( &res.nb[ 2 ], r1 );
395
396	res.data = m_data;
397	return res;
398}
399
400GgmlTensorView::GgmlTensorView( const Tensor& t ) : tensor( t.ggml() ) {}
401#endif