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

KonstantinPerformance tuning on AMD iGPUe78815d

master
3.7 KiB123 linesraw
1// matrix*row vector product, needs first argument reshaped into a sequence of horizontal column major panels
2#ifndef TILE_SIZE
3static const uint TILE_SIZE = 32;
4#endif
5#ifndef THREADS_Y
6static const uint THREADS_Y = 8;
7#endif
8
9// First tensor, reshaped into dense column major horizontal panels of size [ width, TILE_SIZE ]
10Buffer<float> arg0: register( t0 );
11// Second tensor, reshaped into dense column major horizontal panels of size [ width, TILE_SIZE ]
12Buffer<float> arg1: register( t1 );
13// FP32 output tensor, row major and continuous
14RWBuffer<float> result: register( u0 );
15
16cbuffer Constants: register( b0 )
17{
18	uint4 arg0Size: packoffset( c0 );
19	uint arg0panel: packoffset( c1.y );
20	uint2 arg0LayerStrides: packoffset( c1.z );
21	// uint4 arg1Size: packoffset( c2 );
22	uint4 arg1Strides: packoffset( c3 );
23	uint4 resultSize: packoffset( c4 );
24	uint4 resultStrides: packoffset( c5 );
25}
26
27inline uint hadd4( const uint4 v )
28{
29	const uint2 v2 = v.xy + v.zw;
30	return v2.x + v2.y;
31}
32
33inline float hadd4( const float4 v )
34{
35	const float2 v2 = v.xy + v.zw;
36	return v2.x + v2.y;
37}
38
39groupshared float reductionBuffer[ THREADS_Y ][ TILE_SIZE ];
40
41[numthreads( TILE_SIZE, THREADS_Y, 1 )]
42void main( const uint3 group: SV_GroupID, const uint3 thread : SV_GroupThreadID )
43{
44	const uint2 layer = group.yz;
45	// Source offsets for the complete thread group
46	uint2 rsi;
47	rsi.x = group.x * arg0panel + layer.x * arg0LayerStrides.x + layer.y * arg0LayerStrides.y;
48	rsi.y = layer.x * arg1Strides.z + layer.y * arg1Strides.w;
49	// Apply source offsets for this particular thread
50	rsi.x += thread.y * TILE_SIZE + thread.x;
51	rsi.y += thread.y * arg1Strides.x;
52
53	const uint2 rsiInc = uint2( THREADS_Y * TILE_SIZE, THREADS_Y * arg1Strides.x );
54
55	const uint completeTiles = arg0Size.x / ( THREADS_Y * 4 );
56	uint i;
57	float4 acc = 0.0;
58	for( i = 0; i < completeTiles; i++ )
59	{
60		// Each iteration of this loop consumes THREADS_Y*4 columns from the arg0 panel, and THREADS_Y*4 values from arg1
61		float4 v0, v1;
62		[unroll]
63		for( uint j = 0; j < 4; j++, rsi += rsiInc )
64		{
65			// Load [ TILE_SIZE, THREADS_Y ] block from the first source tensor
66			v0[ j ] = arg0[ rsi.x ];
67			// Broadcast [ THREADS_Y ] row from the second source tensor
68			v1[ j ] = arg1[ rsi.y ];
69		}
70
71		// Now we have [ TILE_SIZE, THREADS_Y * 4 ] block from the first source tensor in the v0 vector,
72		// and [ THREADS_Y * 4 ] row from the second one in the v1 vector
73		// Multiply and accumulate.
74		acc = mad( v0, v1, acc );
75	}
76
77	// Handle the remainder columns, if any.
78	// When present, their count is in [ 1 .. THREADS_Y * 4 - 1 ] interval
79	const uint rem = arg0Size.x % ( THREADS_Y * 4 );
80	if( rem != 0 )
81	{
82		float4 v0 = 0.0, v1 = 0.0;
83		[unroll]
84		for( uint j = 0; j < 4; j++, rsi += rsiInc )
85		{
86			const uint x = ( j * THREADS_Y ) + thread.y;
87			if( x < rem )
88			{
89				v0[ j ] = arg0[ rsi.x ];
90				v1[ j ] = arg1[ rsi.y ];
91			}
92		}
93		acc = mad( v0, v1, acc );
94	}
95
96	// We now have [ TILE_SIZE, THREADS_Y * 4 ] block in the local variables of this thread group
97	// The group however only outputs [ TILE_SIZE ] elements max, need a reduction
98	float acc1 = hadd4( acc );
99	reductionBuffer[ thread.y ][ thread.x ] = acc1;
100	GroupMemoryBarrierWithGroupSync();
101
102	for( i = THREADS_Y / 2; i > 1; i /= 2 )
103	{
104		if( thread.y < i )
105		{
106			acc1 += reductionBuffer[ thread.y + i ][ thread.x ];
107			reductionBuffer[ thread.y ][ thread.x ] = acc1;
108		}
109		GroupMemoryBarrierWithGroupSync();
110	}
111
112	if( thread.y != 0 )
113		return;
114
115	const uint resultPos = group.x * TILE_SIZE;
116	const uint outputSize = min( TILE_SIZE, resultSize.x - resultPos );
117	if( thread.x >= outputSize )
118		return;
119
120	const uint4 resultPos4 = uint4( resultPos + thread.x, 0, layer );
121	const uint rdi = hadd4( resultPos4 * resultStrides );
122	result[ rdi ] = acc1 + reductionBuffer[ 1 ][ thread.x ];
123}