@tj57

Как исправить проблему в умножении матриц на OpenCL?

Делаю умножение двух матриц на OpenCL, используя векторный тип данных. Опирался на этот пример: https://cnugteren.github.io/tutorial/pages/page6.html. В моём случае размер локальной группы DX = 8, размер векторного типа WIDTH = 4. Умножаю 2 квадратные матрицы 16 х 16.

#define DX 8
#define WIDTH 4

kernel void mul(global const int4 *a, global const int4 *b, global int4 *c, int l, int m, int n) {

    size_t i = get_global_id(0);
    size_t j = get_global_id(1);

	const int row = get_local_id(0); 
	const int col = get_local_id(1);

	const int globalRow = (DX/WIDTH)*get_group_id(0) + row; 
	const int globalCol = DX*get_group_id(1) + col; 

	__local int4 A_part[DX][DX/WIDTH];
    __local int4 B_part[DX][DX/WIDTH];

	const int numTiles = l/DX;

	int4 value = {
		0, 0, 0, 0 
	};

	int localIdx = DX/4;

	for (int i = 0; i < numTiles; i++) {

		const int localRow = DX/WIDTH*i + row;
		const int localCol = DX*i + col;
		A_part[col][row] = a[localCol*(l/WIDTH) + globalRow];
		B_part[col][row] = b[globalCol*(m/WIDTH) + localRow];

		barrier(CLK_LOCAL_MEM_FENCE);

		int4 A_vector, B_vector;
        int valB;

		for (int k=0; k < DX / WIDTH; k++) {

           B_vector = B_part[col][k];

		   for (int w=0; w<WIDTH; w++) {

				A_vector = A_part[WIDTH*k + w][row];
                 switch (w) {
		case 0: valB = B_vector.x; break;
                case 1: valB = B_vector.y; break;
                case 2: valB = B_vector.z; break;
                case 3: valB = B_vector.w; break;
                }
				value.x += A_vector.x * valB;
				value.y += A_vector.y * valB;
				value.z += A_vector.z * valB;
				value.w += A_vector.w * valB;
		   }
		   
        }
		
		barrier(CLK_LOCAL_MEM_FENCE);
	}

    c[globalCol*(m/WIDTH) + globalRow] = value;
}


Такой вариант успешно и верно работает. Теперь мне нужно улучшить алгоритм, чтобы не было внутреннего цикла по WIDTH, так как это лишние вычисления. Я сделал union из матрицы А, чтобы при её записи в локальную память обращаться к ней как к векторному типу, а при вычислениях - как к скалярному. Не могу до конца понять, как правильно обращаться к матрице А при непосредственном умножении:

#define DX 8
#define WIDTH 4

kernel void mul(global const int4 *a, global const int4 *b, global int4 *c, int l, int m, int n) {

    size_t i = get_global_id(0);
    size_t j = get_global_id(1);

	const int row = get_local_id(0); 
	const int col = get_local_id(1);

	const int globalRow = (DX/WIDTH)*get_group_id(0) + row; 
	const int globalCol = DX*get_group_id(1) + col; 

	union {
		int A_part[DX][DX]; 
		int4 A_part_vector[DX][DX/WIDTH];
	} A_union;

    __local int4 B_part[DX][DX/WIDTH];

	const int numTiles = l/DX;

	int4 value = {
		0, 0, 0, 0 
	};

	int localIdx = DX/4;

	for (int i = 0; i < numTiles; i++) {

		const int localRow = DX/WIDTH*i + row;
		const int localCol = DX*i + col;
		A_union.A_part_vector[col][row] = a[localCol*(l/WIDTH) + globalRow]; // обращение к А как к векторному типу
		B_part[col][row] = b[globalCol*(m/WIDTH) + localRow];

		barrier(CLK_LOCAL_MEM_FENCE);

		int4 B_vector;
        int valB;

		for (int k=0; k < DX; k++) { // теперь цикл не по DX/WIDTH, а только по DX

           B_vector = B_part[col][k];

		   // внутренний цикл по ширине убран

			value.x +=  A_union.A_part[WIDTH][row] * B_vector.x; // обращение к А как к скалярному типу
			value.y +=  A_union.A_part[WIDTH][row] * B_vector.y;
			value.z +=  A_union.A_part[WIDTH][row] * B_vector.z;
			value.w +=  A_union.A_part[WIDTH][row] * B_vector.w;
		   
        }
		
		barrier(CLK_LOCAL_MEM_FENCE);
	}

    c[globalCol*(m/WIDTH) + globalRow] = value;
}


Код компилируется, но выводит неверные значения умноженной матрицы:

5ce45f0a79336073942203.png

Как исправить ошибку в перемножении матрицы ?
  • Вопрос задан
  • 355 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы