[GPGPU, Параллельное программирование] Сравнение времени выполнения алгоритма на CPU и GPU
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Использование CUDA Runtime API для вычислений. Сравнение CPU и GPU вычислений
В данной статье я решил провести сравнение выполнения алгоритма написанного на C++ на центральном и графическом процессоре(выполнение вычислений с помощью Nvidia CUDA Runtime API на поддерживаемом GPU Nvidia). CUDA API позволяет выполнение некоторых вычислений на графическом процессоре. Файл c++ использующий cuda, будет иметь расширение .cu.
Схема работы алгоритма приведена ниже.
Задача алгоритма состоит в том, что найти возможные числа X, при возведении которых в степень degree_of, будет получатся исходное число max_number. Сразу отмечу, что все числа которые будут передаваться GPU, будут хранится в массивах. Алгоритм, выполняемый каждым потоком, имеет приблизительно следующий вид:
int degree_of=2;
int degree_of_max=Number_degree_of_max[0];//Массив хранящий значение максимальной степени числа
int x=thread;//номер выполняемого потока
int max_number=INPUT[0];//Массив хранящий число, которое необходимо получить
int Number=1;
int Degree;
bool BREAK=false;//Переменная для завершения while
while(degree_of<=degree_of_max&&!BREAK){
Number=1;
for(int i=0;i<degree_of;i++){
Number*=x;
Degree=degree_of;
}
if(Number==max_number){
OUT_NUMBER[thread]=X;//OUT_NUMBER Массив хранящий числа которые необходимо возвести в степень Degree для получения исходного числа
OUT_DEGREE[thread]=Degree;// OUT_DEGREE Массив хранящий степень в которую нужно возвести число X для получения исходного числа
}
degree_of++;
//В случае выхода за предел :
if(degree_of>degree_of_max||Number>max_number){
BREAK=true;
}
}
Код для выполнения на CPU
SPL
#include <iostream>
#include<vector>
#include<string>//необходимо для getline
#include<thread>
#include<fstream>
using namespace std;
int Running_thread_counter = 0;
void Upload_to_CPU(unsigned long long *Number, unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT, unsigned long long *max, int THREAD);
void Upload_to_CPU(unsigned long long *Number, unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT, unsigned long long *max, int THREAD) {
int thread = THREAD;
Running_thread_counter++;
unsigned long long MAX_DEGREE_OF = max[0];
int X = thread;
unsigned long long Calculated_number = 1;
unsigned long long DEGREE_OF = 2;
unsigned long long INP = INPUT[0];
Stop[thread] = false;
bool BREAK = false;
if (X != 0 && X != 1) {
while (!BREAK) {
if (DEGREE_OF <= MAX_DEGREE_OF) {
Calculated_number = 1;
for (int counter = 0; counter < DEGREE_OF; counter++) {
Calculated_number *= X;
}
if (Calculated_number == INP) {
Stepn[thread] = DEGREE_OF;
Number[thread] = X;
Stop[thread] = true;
BREAK = true;
}
DEGREE_OF++;
}
else { BREAK = true; }
}
}
}
void Parallelize_to_threads(unsigned long long *Number, unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT, unsigned long long *max, int size);
int main()
{
int size = 1000;
unsigned long long *Number = new unsigned long long[size], *Degree_of = new unsigned long long[size];
unsigned long long *Max_Degree_of = new unsigned long long[1];
unsigned long long *INPUT_NUMBER = new unsigned long long[1];
Max_Degree_of[0] = 7900;
INPUT_NUMBER[0] = 216 * 216 * 216;
ifstream inp("input.txt");
if (inp.is_open()) {
string t;
vector<unsigned long long>IN;
while (getline(inp, t)) {
IN.push_back(stol(t));
}
INPUT_NUMBER[0] = IN[0];//исходное число
Max_Degree_of[0] = IN[1];//значение максимальной степени
}
else {
ofstream error("error.txt");
if (error.is_open()) {
error << "No file " << '"' << "input.txt" << '"' << endl;
error << "Please , create a file" << '"' << "input.txt" << '"' << endl;
error << "One read:input number" << endl;
error << "Two read:input max stepen" << endl;
error << "." << endl;
error.close();
INPUT_NUMBER[0] = 1;
Max_Degree_of[0] = 1;
}
}
//расскометрируйте следующий код , если хотите видеть исходные значения в окне консоли
//cout << INPUT[0] << endl;
bool *Elements_that_need_to_stop = new bool[size];
Parallelize_to_threads(Number, Degree_of, Elements_that_need_to_stop, INPUT_NUMBER, Max_Degree_of, size);
vector<unsigned long long>NUMBER, DEGREEOF;
for (int i = 0; i < size; i++) {
if (Elements_that_need_to_stop[i]) {
if (Degree_of[i] < INPUT_NUMBER[0] && Number[i] < INPUT_NUMBER[0]) {//проверка на ошибки
NUMBER.push_back(Number[i]);
DEGREEOF.push_back(Degree_of[i]);
}
}
}
//расскометрируйте следующий код , если хотите вывести результаты в консоль
//это может замедлить программу
/*
for (int f = 0; f < NUMBER.size(); f++) {
cout << NUMBER[f] << "^" << DEGREEOF[f] << "=" << INPUT_NUMBER[0] << endl;
}
*/
ofstream out("out.txt");
if (out.is_open()) {
for (int f = 0; f < NUMBER.size(); f++) {
out << NUMBER[f] << "^" << DEGREEOF[f] << "=" << INPUT_NUMBER[0] << endl;
}
out.close();
}
}
void Parallelize_to_threads(unsigned long long *Number, unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT, unsigned long long *max, int size) {
thread *T = new thread[size];
Running_thread_counter = 0;
for (int i = 0; i < size; i++) {
T[i] = thread(Upload_to_CPU, Number, Stepn, Stop, INPUT, max, i);
T[i].detach();
}
while (Running_thread_counter < size - 1);//дождаться завершения выполнения всех потоков
}
Для работы алгоритма необходим текстовый файл с исходным числом и максимальной степенью.
Код для выполнения вычислений на GPU
SPL
//библиотеки cuda_runtime.h и device_launch_parameters.h
//для работы с cyda
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include<vector>
#include<string>//для getline
#include <stdio.h>
#include<fstream>
using namespace std;
__global__ void Upload_to_GPU(unsigned long long *Number,unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT,unsigned long long *max) {
int thread = threadIdx.x;
unsigned long long MAX_DEGREE_OF = max[0];
int X = thread;
unsigned long long Calculated_number = 1;
unsigned long long Current_degree_of_number = 2;
unsigned long long Original_numberP = INPUT[0];
Stop[thread] = false;
bool BREAK = false;
if (X!=0&&X!=1) {
while (!BREAK) {
if (Current_degree_of_number <= MAX_DEGREE_OF) {
Calculated_number = 1;
for (int counter = 0; counter < Current_degree_of_number; counter++) {
Calculated_number *=X;
}
if (Calculated_number == Original_numberP) {
Stepn[thread] = Current_degree_of_number;
Number[thread] = X;
Stop[thread] = true;
BREAK = true;
}
Current_degree_of_number++;
}
else { BREAK = true; }
}
}
}
cudaError_t Configure_cuda(unsigned long long *Number, unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT, unsigned long long *max,unsigned int size);
int main()
{
int size = 1000;
unsigned long long *Number=new unsigned long long [size], *Degree_of=new unsigned long long [size];
unsigned long long *Max_degree_of = new unsigned long long [1];
unsigned long long *INPUT_NUMBER = new unsigned long long [1];
Max_degree_of[0] = 7900;
ifstream inp("input.txt");
if (inp.is_open()) {
string text;
vector<unsigned long long>IN;
while (getline(inp, text)) {
IN.push_back( stol(text));
}
INPUT_NUMBER[0] = IN[0];
Max_degree_of[0] = IN[1];
}
else {
ofstream error("error.txt");
if (error.is_open()) {
error<<"No file "<<'"'<<"input.txt"<<'"'<<endl;
error<<"Please , create a file" << '"' << "input.txt" << '"' << endl;
error << "One read:input number" << endl;
error << "Two read:input max stepen" << endl;
error << "." << endl;
error.close();
INPUT_NUMBER[0] = 1;
Max_degree_of[0] = 1;
}
}
bool *Elements_that_need_to_stop = new bool[size];
// Загрузка массивов в cuda
cudaError_t cudaStatus = Configure_cuda(Number, Degree_of, Elements_that_need_to_stop, INPUT_NUMBER, Max_degree_of, size);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "addWithCuda failed!");
return 1;
}
vector<unsigned long long>NUMBER, DEGREEOF;
for (int i = 0; i < size; i++) {
if (Elements_that_need_to_stop[i]) {
NUMBER.push_back(Number[i]);//занести в вектор число
DEGREEOF.push_back(Degree_of[i]);//занести в вектор степень числа
}
}
//раскоментируйте следующий код , чтобы вывести результаты в консоль
/*
for (int f = 0; f < NUMBER.size(); f++) {
cout << NUMBER[f] << "^" << DEGREEOF[f] << "=" << INPUT_NUMBER[0] << endl;
}*/
ofstream out("out.txt");
if (out.is_open()) {
for (int f = 0; f < NUMBER.size(); f++) {
out << NUMBER[f] << "^" << DEGREEOF[f] << "=" << INPUT_NUMBER[0] << endl;
}
out.close();
}
//Очистить ресурсы связанные с устройством
cudaStatus = cudaDeviceReset();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaDeviceReset failed!");
return 1;
}
return 0;
}
cudaError_t Configure_cuda(unsigned long long *Number, unsigned long long *Degree_of, bool *Stop,unsigned long long *INPUT, unsigned long long *max,unsigned int size) {
unsigned long long *dev_Number = 0;
unsigned long long *dev_Degree_of = 0;
unsigned long long *dev_INPUT = 0;
unsigned long long *dev_Max = 0;
bool *dev_Elements_that_need_to_stop;
cudaError_t cudaStatus;
// УСТАНОВКА ИСПОЛЬЗУЕМОГО GPU
cudaStatus = cudaSetDevice(0);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaSetDevice failed! Do you have a CUDA-capable GPU installed?");
goto Error;
}
// РЕЗЕРВИРОВАНИЕ МЕСТА В ПАМЯТИ ПОД ДАННЫЕ
cudaStatus = cudaMalloc((void**)&dev_Number, size * sizeof(unsigned long long));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!dev_Number");
goto Error;
}
cudaStatus = cudaMalloc((void**)&dev_Degree_of, size * sizeof(unsigned long long));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!dev_Degree_of");
goto Error;
}
cudaStatus = cudaMalloc((void**)&dev_Max, size * sizeof(unsigned long long int));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!dev_Max");
goto Error;
}
cudaStatus = cudaMalloc((void**)&dev_INPUT, size * sizeof(unsigned long long));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!dev_INPUT");
goto Error;
}
cudaStatus = cudaMalloc((void**)&dev_Elements_that_need_to_stop, size * sizeof(bool));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!dev_Stop");
goto Error;
}
// ПЕРЕМЕЩЕНИЕ ДАННЫХ В ПАМЯТЬ GPU
cudaStatus = cudaMemcpy(dev_Max, max, size * sizeof(unsigned long long), cudaMemcpyHostToDevice);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
cudaStatus = cudaMemcpy(dev_INPUT, INPUT, size * sizeof(unsigned long long), cudaMemcpyHostToDevice);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
Upload_to_GPU<<<1, size>>>(dev_Number, dev_Degree_of, dev_Elements_that_need_to_stop, dev_INPUT, dev_Max);
// Проверка сбоев ядра
cudaStatus = cudaGetLastError();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "addKernel launch failed: %s\n", cudaGetErrorString(cudaStatus));
goto Error;
}
// Ожидание завершения операций , выполняемых ядром
cudaStatus = cudaDeviceSynchronize();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);
goto Error;
}
// Перемещение данных из памяти GPU в системную память
cudaStatus = cudaMemcpy(Number, dev_Number, size * sizeof(unsigned long long), cudaMemcpyDeviceToHost);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
cudaStatus = cudaMemcpy(Degree_of, dev_Degree_of, size * sizeof(unsigned long long), cudaMemcpyDeviceToHost);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
cudaStatus = cudaMemcpy(Stop, dev_Elements_that_need_to_stop, size * sizeof(bool), cudaMemcpyDeviceToHost);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
Error://Освобождение памяти GPU от данных
cudaFree(dev_INPUT);
cudaFree(dev_Degree_of);
cudaFree(dev_Max);
cudaFree(dev_Elements_that_need_to_stop);
cudaFree(dev_Number);
return cudaStatus;
}
Идентификатор
__global__
в .cu файле указывает, что функция выполняется на уровне ядра GPU.
Для работы с cyda, перед вызовом функции, нужно зарезервировать память под массив и перенести элементы в память GPU. Это увеличивает объем кода, но позволяет разгрузить CPU, так как вычисления производятся на GPU.Поэтому ,cuda, дает как минимум возможность разгрузить процессор для выполнения других нагрузок, не использующих cuda.
В случае примера на cuda, задача процессора заключается лишь в загрузке инструкций на GPU и обработке результатов пришедших с GPU; В то время как в коде для CPU, процессор обрабатывает каждый поток. Стоит отметить, что cyda имеет ограничения по количеству запускаемых потоков, поэтому в обоих алгоритмах я взял одинаковое количество потоков, равное 1000. Также, в случае с CPU я использовал переменную
int Running_thread_counter = 0;
чтобы считать количество уже выполненных потоков и дожидаться, пока все потоки не выполнятся.
Тестируемая конфигурация
SPL
- CPU :amd ryzen 5 1400(4core,8thread)
- ОЗУ:8гбDDR4 2666
- GPU:Nvidia rtx 2060
- OS:windows 10 version 2004
- Cuda:
- Compute Capability 7.5
- Threads per Multiprocessor 1024
- CUDA 11.1.70
- GPU-Z:version 2.35.0
- Visual Studio 2017
Сведения о cyda были взяты из GPU-Z
Для тестирования алгоритма я использовал
следующий код на C#
SPL
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.IO;
namespace ConsoleAppTESTSTEPEN_CPU_AND_GPU_
{
class Program
{
static string Upload(Int64 number,Int64 degree_of)
{
string OUT = "";
string[] Chord_values = new string[2];
Int64 Degree_of = degree_of;
Int64 Number = number;
Chord_values[0] = Number.ToString();
Chord_values[1] = Degree_of.ToString();
File.WriteAllLines("input.txt", Chord_values);//файл входных данных
OUT+="input number:" + Number.ToString()+"\n";
OUT+="input degree of number:" + Degree_of.ToString()+"\n";
DateTime running_CPU_application = DateTime.Now;//записать время запуска программы
Process proc= Process.Start("ConsoleApplication29.exe");//exe реализация алгоритма на c++ x64 использующая CPU для вычислений
while (!proc.HasExited) ;//дождатся завершения программы
DateTime stop_CPU_application = DateTime.Now;//записать время остановки программы
string[]outs = File.ReadAllLines("out.txt");//получить результаты
File.Delete("out.txt");
OUT+="CPU:"+"\n";
if (outs.Length>0)
{
for (int j = 0; j < outs.Length; j++)
{
OUT+=outs[j]+"\n";
}
}
else { OUT+="no values"+"\n"; }
OUT+="running_CPU_application:" + running_CPU_application.ToString()+"\n";
OUT+="stop_CPU_application:" + stop_CPU_application.ToString()+"\n";
OUT+="GPU:"+"\n";
//альтернативные действия для реализации алгоритма korenXN.exe x64 использующего для вычислений GPU
DateTime running_GPU_application = DateTime.Now;
Process procGPU = Process.Start("korenXN.exe");
while (!procGPU.HasExited) ;
DateTime stop_GPU_application = DateTime.Now;
string[] outs2 = File.ReadAllLines("out.txt");
File.Delete("out.txt");
if (outs2.Length > 0)
{
for (int j = 0; j < outs2.Length; j++)
{
OUT+=outs2[j]+"\n";
}
}
else { OUT+="no values"+"\n"; }
OUT+="running_GPU_application:" + running_GPU_application.ToString()+"\n";
OUT+="stop_GPU_application:" + stop_GPU_application.ToString()+"\n";
return OUT;//возвратить результат
}
static void Main()
{
Int64 start = 36*36;//начальное значение входного числа
Int64 degree_of_strat = 500;//начальное значение максимальной степени
int size = 20-5;//количество элементов в массиве
Int64[] Number = new Int64[size];//массив входных чисел
Int64[] Degree_of = new Int64[size];//массив максимальных степеней
string[]outs= new string[size];//масссив результатов
for (int n = 0; n < size; n++)
{
if (n % 2 == 0)
{
Number[n] = start * start;
}
else
{
Number[n] = start * degree_of_strat;
Number[n] -= n + n;
}
start += 36*36;
Degree_of[n] = degree_of_strat;
degree_of_strat +=1000;
}
for (int n = 0; n < size; n++)
{
outs[n] = Upload(Number[n], Degree_of[n]);
Console.WriteLine(outs[n]);
}
System.IO.File.WriteAllLines("result.txt", outs);//записать результаты в файл result.txt
}
}
}
, который создавал файл с исходными данными, затем последовательно запускал exe файлы алгоритмов использующих CPU или GPU и замерял время их работы, затем заносил это время и результаты работы алгоритмов в файл result.txt. Для замера загруженности процессора использовался диспетчер задач windows.
Результаты теста превидены в таблице:
Как видно из таблицы, время выполнения алгоритма на GPU немного больше, чем на CPU.
Однако, отмечу, что вовремя работы алгоритма использующего для вычислений GPU загрузка им CPU, в Диспетчере задач, не превышала 30%, в то время как алгоритм использующий для вычислений CPU, загружал его на 68-85%, что в свою очередь иногда приводило к замедлению других приложений. Также, ниже приведен график, показывающий различие во
времени выполнения (по оси Y)CPU и GPU в зависимости от входного числа(по оси X).
график
SPL
Далее я решил провести тестирование при процессоре нагруженном другими приложениями. Процессор был нагружен так, что запущенный в приложение тест, не занимал больше 55% ресурсов процессора. Результаты теста приведены ниже:
График
SPL
Как видно из таблицы, в случае с нагруженным CPU, выполнение вычислений на GPU, дает прирост производительности, так как загруженность процессора в 30% укладывается в лимит 55%, а в случае использования CPU для вычислений, его загрузка составляет 68-85% , что тормозит работу алгоритма, если CPU нагружен другими приложениями.
Поэтому, можно сделать вывод, что использование GPU для вычислений, не обязательно должно давать более быструю работу алгоритма, однако, оно способно разгрузить CPU, что может играть роль, если он нагружен другими приложениями.
Ресурсы:
SPL
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование, Java] Когда параллельные потоки буксуют
- [Информационная безопасность] Security Week 44: серьезная уязвимость в GeForce Experience
- [Разработка игр, Производство и разработка электроники, Компьютерное железо, Игры и игровые приставки] Ажиотажный спрос на новые карты Nvidia — заслуга не производителя, а косоруких разработчиков игр
- [Процессоры, IT-компании] Аллен Ву — «пешка», ставшая «конем» в шахматной партии США и Китая
- Компания NVIDIA сообщила о задержке поставки драйверов для ядра Linux 5.9
- [Законодательство в IT, Финансы в IT, IT-компании] Основатель ARM требует у правительства Великобритании запрещения сделки с ARM
- [IT-компании, Видеокарты, Высокая производительность] Децифицт видеокарт GeForce RTX 3080 и 3090 продлится до конца текущего года
- [Гаджеты, Компьютерное железо, Искусственный интеллект, Настольные компьютеры] Nvidia представила новую версию одноплатного ПК Jetson Nano всего за $59
- [Управление продуктом, Управление продажами, Компьютерное железо, Видеокарты] NVIDIA отложила старт продаж RTX 3070, чтобы не вызвать ажиотаж
- [Видеокарты, Компьютерное железо, Производство и разработка электроники] Nvidia выпустила версию драйвера Game Ready 456.55 WHQL, который занижает частоты RTX 3080
Теги для поиска: #_gpgpu, #_parallelnoe_programmirovanie (Параллельное программирование), #_cuda, #_gpgpu, #_nvidia, #_parallelnye_vychislenija (параллельные вычисления), #_parallelnoe_programmirovanie (параллельное программирование), #_gpu_vychislenija (gpu вычисления), #_mnogopotochnoe_programmirovanie (многопоточное программирование), #_gpgpu, #_parallelnoe_programmirovanie (
Параллельное программирование
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:43
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Использование CUDA Runtime API для вычислений. Сравнение CPU и GPU вычислений В данной статье я решил провести сравнение выполнения алгоритма написанного на C++ на центральном и графическом процессоре(выполнение вычислений с помощью Nvidia CUDA Runtime API на поддерживаемом GPU Nvidia). CUDA API позволяет выполнение некоторых вычислений на графическом процессоре. Файл c++ использующий cuda, будет иметь расширение .cu. Схема работы алгоритма приведена ниже. Задача алгоритма состоит в том, что найти возможные числа X, при возведении которых в степень degree_of, будет получатся исходное число max_number. Сразу отмечу, что все числа которые будут передаваться GPU, будут хранится в массивах. Алгоритм, выполняемый каждым потоком, имеет приблизительно следующий вид: int degree_of=2;
int degree_of_max=Number_degree_of_max[0];//Массив хранящий значение максимальной степени числа int x=thread;//номер выполняемого потока int max_number=INPUT[0];//Массив хранящий число, которое необходимо получить int Number=1; int Degree; bool BREAK=false;//Переменная для завершения while while(degree_of<=degree_of_max&&!BREAK){ Number=1; for(int i=0;i<degree_of;i++){ Number*=x; Degree=degree_of; } if(Number==max_number){ OUT_NUMBER[thread]=X;//OUT_NUMBER Массив хранящий числа которые необходимо возвести в степень Degree для получения исходного числа OUT_DEGREE[thread]=Degree;// OUT_DEGREE Массив хранящий степень в которую нужно возвести число X для получения исходного числа } degree_of++; //В случае выхода за предел : if(degree_of>degree_of_max||Number>max_number){ BREAK=true; } } Код для выполнения на CPUSPL#include <iostream>
#include<vector> #include<string>//необходимо для getline #include<thread> #include<fstream> using namespace std; int Running_thread_counter = 0; void Upload_to_CPU(unsigned long long *Number, unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT, unsigned long long *max, int THREAD); void Upload_to_CPU(unsigned long long *Number, unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT, unsigned long long *max, int THREAD) { int thread = THREAD; Running_thread_counter++; unsigned long long MAX_DEGREE_OF = max[0]; int X = thread; unsigned long long Calculated_number = 1; unsigned long long DEGREE_OF = 2; unsigned long long INP = INPUT[0]; Stop[thread] = false; bool BREAK = false; if (X != 0 && X != 1) { while (!BREAK) { if (DEGREE_OF <= MAX_DEGREE_OF) { Calculated_number = 1; for (int counter = 0; counter < DEGREE_OF; counter++) { Calculated_number *= X; } if (Calculated_number == INP) { Stepn[thread] = DEGREE_OF; Number[thread] = X; Stop[thread] = true; BREAK = true; } DEGREE_OF++; } else { BREAK = true; } } } } void Parallelize_to_threads(unsigned long long *Number, unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT, unsigned long long *max, int size); int main() { int size = 1000; unsigned long long *Number = new unsigned long long[size], *Degree_of = new unsigned long long[size]; unsigned long long *Max_Degree_of = new unsigned long long[1]; unsigned long long *INPUT_NUMBER = new unsigned long long[1]; Max_Degree_of[0] = 7900; INPUT_NUMBER[0] = 216 * 216 * 216; ifstream inp("input.txt"); if (inp.is_open()) { string t; vector<unsigned long long>IN; while (getline(inp, t)) { IN.push_back(stol(t)); } INPUT_NUMBER[0] = IN[0];//исходное число Max_Degree_of[0] = IN[1];//значение максимальной степени } else { ofstream error("error.txt"); if (error.is_open()) { error << "No file " << '"' << "input.txt" << '"' << endl; error << "Please , create a file" << '"' << "input.txt" << '"' << endl; error << "One read:input number" << endl; error << "Two read:input max stepen" << endl; error << "." << endl; error.close(); INPUT_NUMBER[0] = 1; Max_Degree_of[0] = 1; } } //расскометрируйте следующий код , если хотите видеть исходные значения в окне консоли //cout << INPUT[0] << endl; bool *Elements_that_need_to_stop = new bool[size]; Parallelize_to_threads(Number, Degree_of, Elements_that_need_to_stop, INPUT_NUMBER, Max_Degree_of, size); vector<unsigned long long>NUMBER, DEGREEOF; for (int i = 0; i < size; i++) { if (Elements_that_need_to_stop[i]) { if (Degree_of[i] < INPUT_NUMBER[0] && Number[i] < INPUT_NUMBER[0]) {//проверка на ошибки NUMBER.push_back(Number[i]); DEGREEOF.push_back(Degree_of[i]); } } } //расскометрируйте следующий код , если хотите вывести результаты в консоль //это может замедлить программу /* for (int f = 0; f < NUMBER.size(); f++) { cout << NUMBER[f] << "^" << DEGREEOF[f] << "=" << INPUT_NUMBER[0] << endl; } */ ofstream out("out.txt"); if (out.is_open()) { for (int f = 0; f < NUMBER.size(); f++) { out << NUMBER[f] << "^" << DEGREEOF[f] << "=" << INPUT_NUMBER[0] << endl; } out.close(); } } void Parallelize_to_threads(unsigned long long *Number, unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT, unsigned long long *max, int size) { thread *T = new thread[size]; Running_thread_counter = 0; for (int i = 0; i < size; i++) { T[i] = thread(Upload_to_CPU, Number, Stepn, Stop, INPUT, max, i); T[i].detach(); } while (Running_thread_counter < size - 1);//дождаться завершения выполнения всех потоков } Для работы алгоритма необходим текстовый файл с исходным числом и максимальной степенью. Код для выполнения вычислений на GPUSPL//библиотеки cuda_runtime.h и device_launch_parameters.h
//для работы с cyda #include "cuda_runtime.h" #include "device_launch_parameters.h" #include<vector> #include<string>//для getline #include <stdio.h> #include<fstream> using namespace std; __global__ void Upload_to_GPU(unsigned long long *Number,unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT,unsigned long long *max) { int thread = threadIdx.x; unsigned long long MAX_DEGREE_OF = max[0]; int X = thread; unsigned long long Calculated_number = 1; unsigned long long Current_degree_of_number = 2; unsigned long long Original_numberP = INPUT[0]; Stop[thread] = false; bool BREAK = false; if (X!=0&&X!=1) { while (!BREAK) { if (Current_degree_of_number <= MAX_DEGREE_OF) { Calculated_number = 1; for (int counter = 0; counter < Current_degree_of_number; counter++) { Calculated_number *=X; } if (Calculated_number == Original_numberP) { Stepn[thread] = Current_degree_of_number; Number[thread] = X; Stop[thread] = true; BREAK = true; } Current_degree_of_number++; } else { BREAK = true; } } } } cudaError_t Configure_cuda(unsigned long long *Number, unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT, unsigned long long *max,unsigned int size); int main() { int size = 1000; unsigned long long *Number=new unsigned long long [size], *Degree_of=new unsigned long long [size]; unsigned long long *Max_degree_of = new unsigned long long [1]; unsigned long long *INPUT_NUMBER = new unsigned long long [1]; Max_degree_of[0] = 7900; ifstream inp("input.txt"); if (inp.is_open()) { string text; vector<unsigned long long>IN; while (getline(inp, text)) { IN.push_back( stol(text)); } INPUT_NUMBER[0] = IN[0]; Max_degree_of[0] = IN[1]; } else { ofstream error("error.txt"); if (error.is_open()) { error<<"No file "<<'"'<<"input.txt"<<'"'<<endl; error<<"Please , create a file" << '"' << "input.txt" << '"' << endl; error << "One read:input number" << endl; error << "Two read:input max stepen" << endl; error << "." << endl; error.close(); INPUT_NUMBER[0] = 1; Max_degree_of[0] = 1; } } bool *Elements_that_need_to_stop = new bool[size]; // Загрузка массивов в cuda cudaError_t cudaStatus = Configure_cuda(Number, Degree_of, Elements_that_need_to_stop, INPUT_NUMBER, Max_degree_of, size); if (cudaStatus != cudaSuccess) { fprintf(stderr, "addWithCuda failed!"); return 1; } vector<unsigned long long>NUMBER, DEGREEOF; for (int i = 0; i < size; i++) { if (Elements_that_need_to_stop[i]) { NUMBER.push_back(Number[i]);//занести в вектор число DEGREEOF.push_back(Degree_of[i]);//занести в вектор степень числа } } //раскоментируйте следующий код , чтобы вывести результаты в консоль /* for (int f = 0; f < NUMBER.size(); f++) { cout << NUMBER[f] << "^" << DEGREEOF[f] << "=" << INPUT_NUMBER[0] << endl; }*/ ofstream out("out.txt"); if (out.is_open()) { for (int f = 0; f < NUMBER.size(); f++) { out << NUMBER[f] << "^" << DEGREEOF[f] << "=" << INPUT_NUMBER[0] << endl; } out.close(); } //Очистить ресурсы связанные с устройством cudaStatus = cudaDeviceReset(); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaDeviceReset failed!"); return 1; } return 0; } cudaError_t Configure_cuda(unsigned long long *Number, unsigned long long *Degree_of, bool *Stop,unsigned long long *INPUT, unsigned long long *max,unsigned int size) { unsigned long long *dev_Number = 0; unsigned long long *dev_Degree_of = 0; unsigned long long *dev_INPUT = 0; unsigned long long *dev_Max = 0; bool *dev_Elements_that_need_to_stop; cudaError_t cudaStatus; // УСТАНОВКА ИСПОЛЬЗУЕМОГО GPU cudaStatus = cudaSetDevice(0); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaSetDevice failed! Do you have a CUDA-capable GPU installed?"); goto Error; } // РЕЗЕРВИРОВАНИЕ МЕСТА В ПАМЯТИ ПОД ДАННЫЕ cudaStatus = cudaMalloc((void**)&dev_Number, size * sizeof(unsigned long long)); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaMalloc failed!dev_Number"); goto Error; } cudaStatus = cudaMalloc((void**)&dev_Degree_of, size * sizeof(unsigned long long)); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaMalloc failed!dev_Degree_of"); goto Error; } cudaStatus = cudaMalloc((void**)&dev_Max, size * sizeof(unsigned long long int)); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaMalloc failed!dev_Max"); goto Error; } cudaStatus = cudaMalloc((void**)&dev_INPUT, size * sizeof(unsigned long long)); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaMalloc failed!dev_INPUT"); goto Error; } cudaStatus = cudaMalloc((void**)&dev_Elements_that_need_to_stop, size * sizeof(bool)); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaMalloc failed!dev_Stop"); goto Error; } // ПЕРЕМЕЩЕНИЕ ДАННЫХ В ПАМЯТЬ GPU cudaStatus = cudaMemcpy(dev_Max, max, size * sizeof(unsigned long long), cudaMemcpyHostToDevice); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaMemcpy failed!"); goto Error; } cudaStatus = cudaMemcpy(dev_INPUT, INPUT, size * sizeof(unsigned long long), cudaMemcpyHostToDevice); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaMemcpy failed!"); goto Error; } Upload_to_GPU<<<1, size>>>(dev_Number, dev_Degree_of, dev_Elements_that_need_to_stop, dev_INPUT, dev_Max); // Проверка сбоев ядра cudaStatus = cudaGetLastError(); if (cudaStatus != cudaSuccess) { fprintf(stderr, "addKernel launch failed: %s\n", cudaGetErrorString(cudaStatus)); goto Error; } // Ожидание завершения операций , выполняемых ядром cudaStatus = cudaDeviceSynchronize(); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus); goto Error; } // Перемещение данных из памяти GPU в системную память cudaStatus = cudaMemcpy(Number, dev_Number, size * sizeof(unsigned long long), cudaMemcpyDeviceToHost); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaMemcpy failed!"); goto Error; } cudaStatus = cudaMemcpy(Degree_of, dev_Degree_of, size * sizeof(unsigned long long), cudaMemcpyDeviceToHost); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaMemcpy failed!"); goto Error; } cudaStatus = cudaMemcpy(Stop, dev_Elements_that_need_to_stop, size * sizeof(bool), cudaMemcpyDeviceToHost); if (cudaStatus != cudaSuccess) { fprintf(stderr, "cudaMemcpy failed!"); goto Error; } Error://Освобождение памяти GPU от данных cudaFree(dev_INPUT); cudaFree(dev_Degree_of); cudaFree(dev_Max); cudaFree(dev_Elements_that_need_to_stop); cudaFree(dev_Number); return cudaStatus; } Идентификатор __global__
Для работы с cyda, перед вызовом функции, нужно зарезервировать память под массив и перенести элементы в память GPU. Это увеличивает объем кода, но позволяет разгрузить CPU, так как вычисления производятся на GPU.Поэтому ,cuda, дает как минимум возможность разгрузить процессор для выполнения других нагрузок, не использующих cuda. В случае примера на cuda, задача процессора заключается лишь в загрузке инструкций на GPU и обработке результатов пришедших с GPU; В то время как в коде для CPU, процессор обрабатывает каждый поток. Стоит отметить, что cyda имеет ограничения по количеству запускаемых потоков, поэтому в обоих алгоритмах я взял одинаковое количество потоков, равное 1000. Также, в случае с CPU я использовал переменную int Running_thread_counter = 0;
чтобы считать количество уже выполненных потоков и дожидаться, пока все потоки не выполнятся. Тестируемая конфигурацияSPL
Сведения о cyda были взяты из GPU-Z Для тестирования алгоритма я использовал следующий код на C#SPLusing System;
using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; using System.IO; namespace ConsoleAppTESTSTEPEN_CPU_AND_GPU_ { class Program { static string Upload(Int64 number,Int64 degree_of) { string OUT = ""; string[] Chord_values = new string[2]; Int64 Degree_of = degree_of; Int64 Number = number; Chord_values[0] = Number.ToString(); Chord_values[1] = Degree_of.ToString(); File.WriteAllLines("input.txt", Chord_values);//файл входных данных OUT+="input number:" + Number.ToString()+"\n"; OUT+="input degree of number:" + Degree_of.ToString()+"\n"; DateTime running_CPU_application = DateTime.Now;//записать время запуска программы Process proc= Process.Start("ConsoleApplication29.exe");//exe реализация алгоритма на c++ x64 использующая CPU для вычислений while (!proc.HasExited) ;//дождатся завершения программы DateTime stop_CPU_application = DateTime.Now;//записать время остановки программы string[]outs = File.ReadAllLines("out.txt");//получить результаты File.Delete("out.txt"); OUT+="CPU:"+"\n"; if (outs.Length>0) { for (int j = 0; j < outs.Length; j++) { OUT+=outs[j]+"\n"; } } else { OUT+="no values"+"\n"; } OUT+="running_CPU_application:" + running_CPU_application.ToString()+"\n"; OUT+="stop_CPU_application:" + stop_CPU_application.ToString()+"\n"; OUT+="GPU:"+"\n"; //альтернативные действия для реализации алгоритма korenXN.exe x64 использующего для вычислений GPU DateTime running_GPU_application = DateTime.Now; Process procGPU = Process.Start("korenXN.exe"); while (!procGPU.HasExited) ; DateTime stop_GPU_application = DateTime.Now; string[] outs2 = File.ReadAllLines("out.txt"); File.Delete("out.txt"); if (outs2.Length > 0) { for (int j = 0; j < outs2.Length; j++) { OUT+=outs2[j]+"\n"; } } else { OUT+="no values"+"\n"; } OUT+="running_GPU_application:" + running_GPU_application.ToString()+"\n"; OUT+="stop_GPU_application:" + stop_GPU_application.ToString()+"\n"; return OUT;//возвратить результат } static void Main() { Int64 start = 36*36;//начальное значение входного числа Int64 degree_of_strat = 500;//начальное значение максимальной степени int size = 20-5;//количество элементов в массиве Int64[] Number = new Int64[size];//массив входных чисел Int64[] Degree_of = new Int64[size];//массив максимальных степеней string[]outs= new string[size];//масссив результатов for (int n = 0; n < size; n++) { if (n % 2 == 0) { Number[n] = start * start; } else { Number[n] = start * degree_of_strat; Number[n] -= n + n; } start += 36*36; Degree_of[n] = degree_of_strat; degree_of_strat +=1000; } for (int n = 0; n < size; n++) { outs[n] = Upload(Number[n], Degree_of[n]); Console.WriteLine(outs[n]); } System.IO.File.WriteAllLines("result.txt", outs);//записать результаты в файл result.txt } } } Результаты теста превидены в таблице: Как видно из таблицы, время выполнения алгоритма на GPU немного больше, чем на CPU. Однако, отмечу, что вовремя работы алгоритма использующего для вычислений GPU загрузка им CPU, в Диспетчере задач, не превышала 30%, в то время как алгоритм использующий для вычислений CPU, загружал его на 68-85%, что в свою очередь иногда приводило к замедлению других приложений. Также, ниже приведен график, показывающий различие во времени выполнения (по оси Y)CPU и GPU в зависимости от входного числа(по оси X). графикSPLДалее я решил провести тестирование при процессоре нагруженном другими приложениями. Процессор был нагружен так, что запущенный в приложение тест, не занимал больше 55% ресурсов процессора. Результаты теста приведены ниже: ГрафикSPLКак видно из таблицы, в случае с нагруженным CPU, выполнение вычислений на GPU, дает прирост производительности, так как загруженность процессора в 30% укладывается в лимит 55%, а в случае использования CPU для вычислений, его загрузка составляет 68-85% , что тормозит работу алгоритма, если CPU нагружен другими приложениями. Поэтому, можно сделать вывод, что использование GPU для вычислений, не обязательно должно давать более быструю работу алгоритма, однако, оно способно разгрузить CPU, что может играть роль, если он нагружен другими приложениями. Ресурсы:SPL=========== Источник: habr.com =========== Похожие новости:
Параллельное программирование ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:43
Часовой пояс: UTC + 5