Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Sb98628.pdf
Скачиваний:
6
Добавлен:
13.02.2021
Размер:
978.17 Кб
Скачать

П2. Исходный код хост-программы для умножения двух матриц

#include <stdio.h> #include <stdlib.h> #include <math.h> #include "CL/opencl.h" #include "AOCL_Utils.h"

using namespace aocl_utils;

// Конфигурация среды исполнения OpenCL cl_platform_id platform = NULL;

unsigned num_devices = 0; // Число устройств scoped_array<cl_device_id> device; // num_devices элементов cl_context context = NULL; // Контекст исполнения scoped_array<cl_command_queue> queue;// num_devices элементов cl_program program = NULL; // Программа устройства scoped_array<cl_kernel> kernel; // num_devices элементов scoped_array<cl_mem> input_a_buf; // num_devices элементов scoped_array<cl_mem> input_b_buf; // num_devices элементов scoped_array<cl_mem> output_buf; // num_devices элементов long BLOCK_SIZE = 16; // Размер блока

long SIZE = 64; // Множитель размера матриц

//Размеры матриц unsigned A_height; unsigned A_width;

const unsigned &B_height = A_width; unsigned B_width;

const unsigned &C_height = A_height; const unsigned &C_width = B_width;

//Входные и выходные массивы

scoped_array<scoped_aligned_ptr<int> > input_a; // num_devices элементов

scoped_aligned_ptr<int> input_b; scoped_array<scoped_aligned_ptr<int> > output; // num_devices

элементов

scoped_array<int> ref_output;

scoped_array<unsigned> rows_per_device; // num_devices

элементов // Прототипы функций

int rand_int(); bool init_opencl(); void init_problem(); void run();

void compute_reference(); void verify();

void cleanup();

int main(int argc, char **argv) { if (argc > 1)

BLOCK_SIZE = strtol(argv[1], NULL, 0); if (argc > 2)

SIZE = strtol(argv[2], NULL, 0);

38

A_height = SIZE * BLOCK_SIZE;

A_width = SIZE * BLOCK_SIZE;

B_width = SIZE * BLOCK_SIZE;

printf("Matrix sizes:\n A: %d x %d\n B: %d x %d\n C: %d x %d\n",A_height, A_width, B_height, B_width, C_height, C_width);

//Инициализировать OpenCL if(!init_opencl()) { return -1;

}

//Инициализировать данные задачи

//На этом этапе необходимо знать число доступных устройств init_problem();

run();//Запустить ядро

cleanup();// Освободить выделенные ресурсы return 0;

}

/////// ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ///////

//Генерация целого числа в интервале от -10 до 10 int rand_int() {

return (rand() % 20) - 10;

}

//Инициализация OpenCL

bool init_opencl() { cl_int status;

printf("Initializing OpenCL\n"); if(!setCwdToExeDir()) {

return false;

}

// Найти платформу OpenCL platform = findPlatform("Altera"); if(platform == NULL) {

printf("ERROR: Unable to find Intel(R) FPGA OpenCL platform.\n");

return false;

}

// Запросить доступные устройства OpenCL

device.reset(getDevices(platform, CL_DEVICE_TYPE_ALL, &num_devices));

printf("Platform: %s\n", getPlatformName(platform).c_str()); printf("Using %d device(s)\n", num_devices);

for(unsigned i = 0; i < num_devices; ++i) {

printf(" %s\n", getDeviceName(device[i]).c_str());

}

context = clCreateContext(NULL, num_devices, device, NULL, NULL, &status);

checkError(status, "Failed to create context"); // Создать программу исполнения устройства

std::string binary_file = getBoardBinaryFile("mmul", device[0]);

printf("Using AOCX: %s\n", binary_file.c_str());

39

program = createProgramFromBinary(context, binary_file.c_str(), device, num_devices);

// Построить программу

status = clBuildProgram(program, 0, NULL, "", NULL, NULL); сheckError(status, "Failed to build program");

//Создать объекты OpenCL для каждого устройства queue.reset(num_devices); kernel.reset(num_devices); rows_per_device.reset(num_devices); input_a_buf.reset(num_devices); input_b_buf.reset(num_devices); output_buf.reset(num_devices);

//Число блоков строк для обработки

const unsigned num_block_rows = C_height / BLOCK_SIZE;

//Цикл по всем доступным устройствам OpenCL for(unsigned i = 0; i < num_devices; ++i) {

//Очередь команд

queue[i] = clCreateCommandQueue(context, device[i], CL_QUEUE_PROFILING_ENABLE, &status);

checkError(status, "Failed to create command queue"); const char *kernel_name = "mmul";//Ядро

kernel[i] = clCreateKernel(program, kernel_name, &status); checkError(status, "Failed to create kernel");

//Определить число строк, обрабатываемых устройством

//Сначала в блоках строк

rows_per_device[i] = num_block_rows / num_devices;

//Распределить блоки строк по устройствам if(i < (num_block_rows % num_devices)) { rows_per_device[i]++;

}

//Умножить на размер блока для получения числа строк rows_per_device[i] *= BLOCK_SIZE;

//Входные буферы

//Каждому устройству нужны только строки матрицы А,

//соответствующие строкам выходной матрицы

input_a_buf[i] = clCreateBuffer(context, CL_MEM_READ_ONLY, rows_per_device[i] * A_width * sizeof(int), NULL, &status); checkError(status, "Failed to create buffer for input A");

// Каждому устройству потребуется матрица В целиком input_b_buf[i] = clCreateBuffer(context, CL_MEM_READ_ONLY,

B_height * B_width * sizeof(int), NULL, &status); checkError(status, "Failed to create buffer for input B");

//Выходной буфер (только те строки матрицы С,

//которые будут вычисляться на данном устойстве)

output_buf[i] = clCreateBuffer(context, CL_MEM_WRITE_ONLY, rows_per_device[i] * C_width * sizeof(int), NULL, &status); checkError(status, "Failed to create buffer for output");

}

return true;

}

// Инициализация данных задачи

40

//Число устройств должно быть известным void init_problem() {

if(num_devices == 0) { checkError(-1, "No devices");

}

//Сгенерировать входные матрицы А и В

//Матрица А делится на все устройства

//Матрица В используется целиком каждым устройством,

//поэтому ее делить не надо

printf("Generating input matrices\n"); input_a.reset(num_devices); output.reset(num_devices);

for(unsigned i = 0; i < num_devices; ++i) { input_a[i].reset(rows_per_device[i] * A_width); output[i].reset(rows_per_device[i] * C_width); for(unsigned j = 0; j < rows_per_device[i] * A_width;++j)

{

input_a[i][j] = rand_int();

}

}

input_b.reset(B_height * B_width);

for(unsigned i = 0; i < B_height * B_width; ++i) { input_b[i] = rand_int();

}

}

void run() {// Запуск ядра cl_int status;

// Передать входные данные на устройства for(unsigned i = 0; i < num_devices; ++i) {

status = clEnqueueWriteBuffer(queue[i], input_a_buf[i], CL_FALSE,0, rows_per_device[i] * A_width * sizeof(int), input_a[i], 0, NULL, NULL);

checkError(status, "Failed to transfer input A"); status = clEnqueueWriteBuffer(queue[i], input_b_buf[i],

CL_FALSE,0, B_width * B_height * sizeof(int), input_b, 0, NULL, NULL);

checkError(status, "Failed to transfer input B");

}

//Подождать окончания передачи for(unsigned i = 0; i < num_devices; ++i) {

clFinish(queue[i]);

}

//Подготовка к запуску ядер

scoped_array<cl_event> kernel_event(num_devices); const double start_time = getCurrentTimestamp();

// Цикл по всем устройствам

for(unsigned i = 0; i < num_devices; ++i) {

// Установить аргументы ядра unsigned argi = 0;

status = clSetKernelArg(kernel[i], argi++, sizeof(cl_mem), &output_buf[i]);

41

checkError(status, "Failed to set argument %d", argi - 1); status = clSetKernelArg(kernel[i], argi++, sizeof(cl_mem),

&input_a_buf[i]);

checkError(status, "Failed to set argument %d", argi - 1); status = clSetKernelArg(kernel[i], argi++, sizeof(cl_mem),

&input_b_buf[i]);

checkError(status, "Failed to set argument %d", argi - 1); status = clSetKernelArg(kernel[i], argi++, sizeof(A_height),

&A_height);

checkError(status, "Failed to set argument %d", argi - 1); status = clSetKernelArg(kernel[i], argi++, sizeof(B_width),

&B_width);

checkError(status, "Failed to set argument %d", argi - 1); status = clSetKernelArg(kernel[i], argi++, sizeof(A_width),

&A_width);

checkError(status, "Failed to set argument %d", argi - 1);

//Поставить ядро в очередь на исполнение

//Глобальный размер работы равен размеру выходной матрицы

//Локальный размер работы - один блок,

//т. е. BLOCK_SIZE * BLOCK_SIZE

//Чтобы удостовериться, что ядра не начнут

//исполняться до того, как закончится запись во входные

//буферы, используется механизм событий

const size_t global_work_size[2] = {C_width, rows_per_device[i]};

const size_t local_work_size[2] = {BLOCK_SIZE, BLOCK_SIZE}; printf("Launching for device %d (global size: %zd, %zd)\n",

i, global_work_size[0], global_work_size[1]);

status = clEnqueueNDRangeKernel(queue[i], kernel[i], 2, NULL,global_work_size, local_work_size, 0, NULL, &kernel_event[i]);

checkError(status, "Failed to launch kernel");

}

//Дождаться окончания исполнения всех ядер clWaitForEvents(num_devices, kernel_event); const double end_time = getCurrentTimestamp(); const double total_time = end_time - start_time;

//Вывести общее время исполнения всех ядер printf("\nTime: %0.3f ms\n", total_time * 1e3);

//Получить время исполнения ядра с помощью

//профилировочного API OpenCL for(unsigned i = 0; i < num_devices; ++i) {

cl_ulong time_ns = getStartEndTime(kernel_event[i]); printf("Kernel time (device %d): %0.3f ms\n", i, dou-

ble(time_ns) * 1e-6);

}

//Вычислить производительность (в GFLOPS)

//Всего вычислено C_width × C_height значений,

//каждое из которых получено путем умножений и сложений const float flops = (float)(2.0f * C_width * C_height *

A_width / total_time);

42

printf("\nThroughput: %0.2f GFLOPS\n\n", flops * 1e-9); for(unsigned i = 0; i < num_devices; ++i) {

clReleaseEvent(kernel_event[i]);

}

// Прочитать результат

for(unsigned i = 0; i < num_devices; ++i) {

status = clEnqueueReadBuffer(queue[i], output_buf[i], CL_TRUE,0, rows_per_device[i] * C_width * sizeof(int), output[i], 0, NULL, NULL);

checkError(status, "Failed to read output matrix");

}

// Проверить результат compute_reference(); verify();

}

// Вычисление референсного результата void compute_reference() { printf("Computing reference output\n"); ref_output.reset(C_height * C_width);

for(unsigned y = 0, dev_index = 0; y < C_height; ++dev_index)

{

for(unsigned yy = 0; yy < rows_per_device[dev_index];++yy,++y)

{

for(unsigned x = 0; x < C_width; ++x) { int sum = 0;

for(unsigned k = 0; k < A_width; ++k) {

sum += input_a[dev_index][yy * A_width + k] * input_b[k * B_width + x];

}

ref_output[y * C_width + x] = sum;

}

}

}

}

// Сравнение референсного результата с полученным void verify() {

printf("Verifying\n"); bool pass = true;

for(unsigned i = 0; i < num_devices && pass; ++i) for(unsigned j = 0; j < rows_per_device[i] && pass; ++j)

for (unsigned k = 0; k < C_width; ++k) {

if (output[i][j*C_width + k] != ref_output[(i*num_devices+j)*C_width + k]) {

pass = false;

printf("Failed verification @ device %d, row %d, index %d, output = %d, ref = %d\n", i, j, k, output[i][j*C_width + k], ref_output[(i*num_devices+j)*C_width + k]);

break;

}

}

printf("Verification: %s\n", pass ? "PASS" : "FAIL");

43

}

void cleanup() {

for(unsigned i = 0; i < num_devices; ++i) { if(kernel && kernel[i]) {

clReleaseKernel(kernel[i]);

}

if(queue && queue[i]) { clReleaseCommandQueue(queue[i]);

}

if(input_a_buf && input_a_buf[i]) { clReleaseMemObject(input_a_buf[i]);

}

if(input_b_buf && input_b_buf[i]) { clReleaseMemObject(input_b_buf[i]);

}

if(output_buf && output_buf[i]) { clReleaseMemObject(output_buf[i]);

}

}

if(program) { clReleaseProgram(program);

}

if(context) { clReleaseContext(context);

}

}

44

 

СОДЕРЖАНИЕ

 

ВВЕДЕНИЕ..............................................................................................................

3

1. ПРЕДПОСЫЛКИ ВОЗНИКНОВЕНИЯ ЯЗЫКА OPENCL..........................

4

2. ДИЗАЙН OPENCL............................................................................................

5

2.1.

Модель платформы.......................................................................................

6

2.2.

Модель вычислений .....................................................................................

6

2.3.

Модель памяти..............................................................................................

7

2.4.

Модель программирования..........................................................................

8

3. АППАРАТНЫЕ СРЕДСТВА, ПОДДЕРЖИВАЮЩИЕ

 

ПАРАЛЛЕЛЬНЫЕ ВЫЧИСЛЕНИЯ.....................................................................

9

4. ЛАБОРАТОРНЫЕ РАБОТЫ.........................................................................

11

4.1. Поток проектирования при работе с языком OpenCL............................

11

4.1.1.Задание.....................................................................................................

11

4.1.2.Программное и аппаратное обеспечение.............................................

11

4.1.3.Последовательность выполнения работы............................................

12

4.1.4.Заключение по практическому эксперименту.....................................

18

4.1.5.Содержание отчета.................................................................................

19

4.2. Создание аппаратно-программной системы с ОС Linux.

 

Подключение к Ethernet. Работа с Web-сервером............................................

19

4.2.1.Задание.....................................................................................................

20

4.2.2.Последовательность выполнения работы............................................

20

4.2.3.Содержание отчета.................................................................................

25

4.3. Оптимизация умножения матриц в OpenCL............................................

25

4.3.1.Базовый алгоритм умножения матриц.................................................

25

4.3.2.Использование локальной памяти........................................................

27

4.3.3.Увеличение числа одновременно исполняемых рабочих элементов30

4.3.4.Содержание отчета.................................................................................

31

СПИСОК ЛИТЕРАТУРЫ.....................................................................................

32

ИНТЕРНЕТ-РЕСУРСЫ ........................................................................................

32

ПРИЛОЖЕНИЯ.....................................................................................................

33

П1. Код хост-программы для сложения двух векторов...................................

33

П2. Исходный код хост-программы для умножения двух матриц.................

38

45

Грушвицкий Ростислав Игоревич, Кондорский Ярослав Александрович, Перевертайло Ульяна Викторовна, Шарагина Наталья Сергеевна

Язык OpenCL. Практическое знакомство

Учебно-методическое пособие

Редактор Е. А. Ушакова

__________________________________________________________________

Подписано в печать 22.11.19. Формат 60×84 1/16. Бумага офсетная. Печать цифровая. Печ. л. 3,0.

Гарнитура «Times New Roman». Тираж 46 экз. Заказ 157.

__________________________________________________________________

Издательство СПбГЭТУ «ЛЭТИ» 197376, С.-Петербург, ул. Проф. Попова, 5

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]