yum-mirror/VkFFT

git clone https://git.yummers.dev/yum-mirror/VkFFT

Tolmachev DmitriiVkFFT v1.3.2 release3fa0c21

master
11.6 KiB279 linesraw
1//general parts
2#include <stdio.h>
3#include <vector>
4#include <memory>
5#include <string.h>
6#include <chrono>
7#include <thread>
8#include <iostream>
9#ifndef __STDC_FORMAT_MACROS
10#define __STDC_FORMAT_MACROS
11#endif
12#include <inttypes.h>
13
14#if(VKFFT_BACKEND==0)
15#include "vulkan/vulkan.h"
16#include "glslang_c_interface.h"
17#elif(VKFFT_BACKEND==1)
18#include <cuda.h>
19#include <cuda_runtime.h>
20#include <nvrtc.h>
21#include <cuda_runtime_api.h>
22#include <cuComplex.h>
23#elif(VKFFT_BACKEND==2)
24#ifndef __HIP_PLATFORM_HCC__
25#define __HIP_PLATFORM_HCC__
26#endif
27#include <hip/hip_runtime.h>
28#include <hip/hiprtc.h>
29#include <hip/hip_runtime_api.h>
30#include <hip/hip_complex.h>
31#elif(VKFFT_BACKEND==3)
32#ifndef CL_USE_DEPRECATED_OPENCL_1_2_APIS
33#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
34#endif
35#ifdef __APPLE__
36#include <OpenCL/opencl.h>
37#else
38#include <CL/cl.h>
39#endif 
40#elif(VKFFT_BACKEND==4)
41#include <ze_api.h>
42#elif(VKFFT_BACKEND==5)
43#include "Foundation/Foundation.hpp"
44#include "QuartzCore/QuartzCore.hpp"
45#include "Metal/Metal.hpp"
46#endif
47#include "vkFFT.h"
48#include "utils_VkFFT.h"
49#include "half.hpp"
50
51VkFFTResult user_benchmark_VkFFT(VkGPU* vkGPU, uint64_t file_output, FILE* output, uint64_t isCompilerInitialized, VkFFTUserSystemParameters* userParams)
52{
53	VkFFTResult resFFT = VKFFT_SUCCESS;
54#if(VKFFT_BACKEND==0)
55	VkResult res = VK_SUCCESS;
56#elif(VKFFT_BACKEND==1)
57	cudaError_t res = cudaSuccess;
58#elif(VKFFT_BACKEND==2)
59	hipError_t res = hipSuccess;
60#elif(VKFFT_BACKEND==3)
61	cl_int res = CL_SUCCESS;
62#elif(VKFFT_BACKEND==4)
63	ze_result_t res = ZE_RESULT_SUCCESS;
64#elif(VKFFT_BACKEND==5)
65#endif
66	const int num_runs = 3;
67	double benchmark_result = 0;//averaged result = sum(system_size/iteration_time)/num_benchmark_samples
68	//memory allocated on the CPU once, makes benchmark completion faster + avoids performance issues connected to frequent allocation/deallocation.
69	uint64_t storageComplexSize=8;
70	switch (userParams->P) {
71	case 0:
72		storageComplexSize = (2 * sizeof(float));
73		break;
74	case 1:
75		storageComplexSize = (2 * sizeof(double));
76		break;
77	case 2:
78		storageComplexSize = (2 * 2);
79		break;
80	case 3:
81		storageComplexSize = (4 * sizeof(double));
82		break;
83	default:
84		storageComplexSize = (2 * sizeof(float));
85		break;
86	}
87    uint64_t bufferSize = 0;
88    if (userParams->R2C) {
89        bufferSize = (uint64_t)(storageComplexSize / 2) * (userParams->X + 2) * userParams->Y * userParams->Z * userParams->B;
90    }
91    else {
92        if (userParams->DCT) {
93            bufferSize = (uint64_t)(storageComplexSize / 2) * userParams->X * userParams->Y * userParams->Z * userParams->B;
94        }
95        else {
96            bufferSize = (uint64_t)storageComplexSize * userParams->X * userParams->Y * userParams->Z * userParams->B;
97        }
98    }
99    
100    float* buffer_input = (float*)malloc(bufferSize);
101    if (!buffer_input) return VKFFT_ERROR_MALLOC_FAILED;
102    for (uint64_t i = 0; i < bufferSize/sizeof(float); i++) {
103        buffer_input[i] = (float)(2 * ((float)rand()) / RAND_MAX - 1.0);
104    }
105	for (uint64_t n = 0; n < 2; n++) {
106		double run_time[num_runs];
107		for (uint64_t r = 0; r < num_runs; r++) {
108			//Configuration + FFT application .
109			VkFFTConfiguration configuration = {};
110			VkFFTApplication app = {};
111			//FFT + iFFT sample code.
112			//Setting up FFT configuration for forward and inverse FFT.
113			configuration.FFTdim = 1; //FFT dimension, 1D, 2D or 3D (default 1).
114			//if (n == 1) configuration.keepShaderCode = 1;
115
116			configuration.size[0] = userParams->X;
117			configuration.size[1] = userParams->Y;
118			configuration.size[2] = userParams->Z;
119			if (userParams->Y > 1) configuration.FFTdim++;
120			if (userParams->Z > 1) configuration.FFTdim++;
121			configuration.numberBatches = userParams->B;
122			configuration.performR2C = userParams->R2C;
123			configuration.performDCT = userParams->DCT;
124			if (userParams->P == 1) configuration.doublePrecision = 1;
125			if (userParams->P == 2) configuration.halfPrecision = 1;
126			if (userParams->P == 3) configuration.quadDoubleDoublePrecision = 1;
127#if(VKFFT_BACKEND!=5)
128			if (userParams->saveApplicationToString && (n==0) && (r==0)) configuration.saveApplicationToString = 1;
129			if (userParams->loadApplicationFromString || (userParams->saveApplicationToString && ((n != 0) || (r != 0)))) configuration.loadApplicationFromString = 1;
130#endif
131			//After this, configuration file contains pointers to Vulkan objects needed to work with the GPU: VkDevice* device - created device, [uint64_t *bufferSize, VkBuffer *buffer, VkDeviceMemory* bufferDeviceMemory] - allocated GPU memory FFT is performed on. [uint64_t *kernelSize, VkBuffer *kernel, VkDeviceMemory* kernelDeviceMemory] - allocated GPU memory, where kernel for convolution is stored.
132#if(VKFFT_BACKEND==5)
133			configuration.device = vkGPU->device;
134#else
135            configuration.device = &vkGPU->device;
136#endif
137#if(VKFFT_BACKEND==0)
138			configuration.queue = &vkGPU->queue; //to allocate memory for LUT, we have to pass a queue, vkGPU->fence, commandPool and physicalDevice pointers 
139			configuration.fence = &vkGPU->fence;
140			configuration.commandPool = &vkGPU->commandPool;
141			configuration.physicalDevice = &vkGPU->physicalDevice;
142			configuration.isCompilerInitialized = isCompilerInitialized;//compiler can be initialized before VkFFT plan creation. if not, VkFFT will create and destroy one after initialization
143#elif(VKFFT_BACKEND==3)
144			configuration.context = &vkGPU->context;
145#elif(VKFFT_BACKEND==4)
146			configuration.context = &vkGPU->context;
147			configuration.commandQueue = &vkGPU->commandQueue;
148			configuration.commandQueueID = vkGPU->commandQueueID;
149#elif(VKFFT_BACKEND==5)
150            configuration.queue = vkGPU->queue;
151#endif
152			//Allocate buffer for the input data.
153#if(VKFFT_BACKEND==0)
154			VkBuffer buffer = {};
155			VkDeviceMemory bufferDeviceMemory = {};
156			resFFT = allocateBuffer(vkGPU, &buffer, &bufferDeviceMemory, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_HEAP_DEVICE_LOCAL_BIT, bufferSize);
157			if (resFFT != VKFFT_SUCCESS) return resFFT;
158			configuration.buffer = &buffer;
159#elif(VKFFT_BACKEND==1)
160			cuFloatComplex* buffer = 0;
161			res = cudaMalloc((void**)&buffer, bufferSize);
162			if (res != cudaSuccess) return VKFFT_ERROR_FAILED_TO_ALLOCATE;
163			configuration.buffer = (void**)&buffer;
164#elif(VKFFT_BACKEND==2)
165			hipFloatComplex* buffer = 0;
166			res = hipMalloc((void**)&buffer, bufferSize);
167			if (res != hipSuccess) return VKFFT_ERROR_FAILED_TO_ALLOCATE;
168			configuration.buffer = (void**)&buffer;
169#elif(VKFFT_BACKEND==3)
170			cl_mem buffer = 0;
171			buffer = clCreateBuffer(vkGPU->context, CL_MEM_READ_WRITE, bufferSize, 0, &res);
172			if (res != CL_SUCCESS) return VKFFT_ERROR_FAILED_TO_ALLOCATE;
173			configuration.buffer = &buffer;
174#elif(VKFFT_BACKEND==4)
175			void* buffer = 0;
176			ze_device_mem_alloc_desc_t device_desc = {};
177			device_desc.stype = ZE_STRUCTURE_TYPE_DEVICE_MEM_ALLOC_DESC;
178			res = zeMemAllocDevice(vkGPU->context, &device_desc, bufferSize, sizeof(float), vkGPU->device, &buffer);
179			if (res != ZE_RESULT_SUCCESS) return VKFFT_ERROR_FAILED_TO_ALLOCATE;
180			configuration.buffer = &buffer;
181#elif(VKFFT_BACKEND==5)
182            MTL::Buffer* buffer = 0;
183            buffer = vkGPU->device->newBuffer(bufferSize, MTL::ResourceStorageModePrivate);
184            configuration.buffer = &buffer;
185#endif
186
187			configuration.bufferSize = &bufferSize;
188            
189            resFFT = transferDataFromCPU(vkGPU, buffer_input, &buffer, bufferSize);
190            if (resFFT != VKFFT_SUCCESS) return resFFT;
191            
192			if (configuration.loadApplicationFromString) {
193				FILE* kernelCache;
194				uint64_t str_len;
195				char fname[500];
196				int VkFFT_version = VkFFTGetVersion();
197				sprintf(fname, "VkFFT_binary_X%" PRIu64 "_Y%" PRIu64 "_Z%" PRIu64 "_P%" PRIu64 "_B%" PRIu64 "_N%" PRIu64 "_R2C%" PRIu64 "_DCT%" PRIu64 "_ver%d", userParams->X, userParams->Y, userParams->Z, userParams->P, userParams->B, userParams->N, userParams->R2C, userParams->DCT, VkFFT_version);
198				kernelCache = fopen(fname, "rb");
199				if (!kernelCache) return VKFFT_ERROR_EMPTY_FILE;
200				fseek(kernelCache, 0, SEEK_END);
201				str_len = ftell(kernelCache);
202				fseek(kernelCache, 0, SEEK_SET);
203				configuration.loadApplicationString = malloc(str_len);
204				fread(configuration.loadApplicationString, str_len, 1, kernelCache);
205				fclose(kernelCache);
206			}
207			//Initialize applications. This function loads shaders, creates pipeline and configures FFT based on configuration file. No buffer allocations inside VkFFT library.  
208			resFFT = initializeVkFFT(&app, configuration);
209			if (resFFT != VKFFT_SUCCESS) return resFFT;
210
211			if (configuration.loadApplicationFromString)
212				free(configuration.loadApplicationString);
213
214			if (configuration.saveApplicationToString) {
215				FILE* kernelCache;
216				char fname[500];
217				int VkFFT_version = VkFFTGetVersion();
218				sprintf(fname, "VkFFT_binary_X%" PRIu64 "_Y%" PRIu64 "_Z%" PRIu64 "_P%" PRIu64 "_B%" PRIu64 "_N%" PRIu64 "_R2C%" PRIu64 "_DCT%" PRIu64 "_ver%d", userParams->X, userParams->Y, userParams->Z, userParams->P, userParams->B, userParams->N, userParams->R2C, userParams->DCT, VkFFT_version);
219				kernelCache = fopen(fname, "wb");
220				fwrite(app.saveApplicationString, app.applicationStringSize, 1, kernelCache);
221				fclose(kernelCache);
222			}
223
224			//Submit FFT+iFFT.
225			
226			double totTime = 0;
227
228			VkFFTLaunchParams launchParams = {};
229			resFFT = performVulkanFFTiFFT(vkGPU, &app, &launchParams, userParams->N, &totTime);
230			if (resFFT != VKFFT_SUCCESS) return resFFT;
231			run_time[r] = totTime;
232			if (n > 0) {
233				if (r == num_runs - 1) {
234					double std_error = 0;
235					double avg_time = 0;
236					for (uint64_t t = 0; t < num_runs; t++) {
237						avg_time += run_time[t];
238					}
239					avg_time /= num_runs;
240					for (uint64_t t = 0; t < num_runs; t++) {
241						std_error += (run_time[t] - avg_time) * (run_time[t] - avg_time);
242					}
243					std_error = sqrt(std_error / num_runs);
244					uint64_t num_tot_transfers = 0;
245					for (uint64_t i = 0; i < configuration.FFTdim; i++)
246						num_tot_transfers += app.localFFTPlan->numAxisUploads[i];
247					num_tot_transfers *= 4;
248					if (file_output)
249						fprintf(output, "VkFFT System: %" PRIu64 "x%" PRIu64 "x%" PRIu64 " Batch: %" PRIu64 " Buffer: %" PRIu64 " MB avg_time_per_step: %0.3f ms std_error: %0.3f num_iter: %" PRIu64 " benchmark: %" PRIu64 " scaled bandwidth: %0.1f bandwidth: %0.1f\n", configuration.size[0], configuration.size[1], configuration.size[2], userParams->B, bufferSize / 1024 / 1024, avg_time, std_error, userParams->N, (uint64_t)(((double)bufferSize / 1024) / avg_time), bufferSize / 1024.0 / 1024.0 / 1.024 * 4 * configuration.FFTdim / avg_time, bufferSize / 1024.0 / 1024.0 / 1.024 * num_tot_transfers / avg_time);
250
251					printf("VkFFT System: %" PRIu64 "x%" PRIu64 "x%" PRIu64 " Batch: %" PRIu64 " Buffer: %" PRIu64 " MB avg_time_per_step: %0.3f ms std_error: %0.3f num_iter: %" PRIu64 " benchmark: %" PRIu64 " scaled bandwidth: %0.1f real bandwidth: %0.1f\n", configuration.size[0], configuration.size[1], configuration.size[2], userParams->B, bufferSize / 1024 / 1024, avg_time, std_error, userParams->N, (uint64_t)(((double)bufferSize / 1024) / avg_time), bufferSize / 1024.0 / 1024.0 / 1.024 * 4 * configuration.FFTdim / avg_time, bufferSize / 1024.0 / 1024.0 / 1.024 * num_tot_transfers / avg_time);
252					benchmark_result += ((double)bufferSize / 1024) / avg_time;
253				}
254
255
256			}
257
258#if(VKFFT_BACKEND==0)
259			vkDestroyBuffer(vkGPU->device, buffer, NULL);
260			vkFreeMemory(vkGPU->device, bufferDeviceMemory, NULL);
261#elif(VKFFT_BACKEND==1)
262			cudaFree(buffer);
263#elif(VKFFT_BACKEND==2)
264			hipFree(buffer);
265#elif(VKFFT_BACKEND==3)
266			clReleaseMemObject(buffer);
267#elif(VKFFT_BACKEND==4)
268			zeMemFree(vkGPU->context, buffer);
269#elif(VKFFT_BACKEND==5)
270            buffer->release();
271#endif
272
273			deleteVkFFT(&app);
274
275		}
276	}
277    free(buffer_input);
278	return resFFT;
279}