yum/vfft
git clone https://git.yummers.dev/yum/vfft
c8008fe
master
1#define VKFFT_BACKEND 0 // vulkan 2 3#include "fftw3.h" 4#include "vkFFT.h" 5 6#include <stdio.h> 7#include <string.h> 8#include <unistd.h> 9 10#include <chrono> 11#include <vector> 12 13using std::chrono::high_resolution_clock ; 14using std::chrono::duration_cast ; 15using std::chrono::duration ; 16using std::chrono::milliseconds ; 17 18#define CHECK (expr ) do { \ 19 auto err = (expr); \ 20 if (err != 0) { \ 21 fprintf(stderr, "%s failed: %d\n", #expr, (int) err); \ 22 exit(1); \ 23 } \ 24} while (0) 25 26#define CHECK_TRUE (expr ) do { \ 27 auto ret = (expr); \ 28 if (!ret) { \ 29 fprintf(stderr, "%s failed: %d\n", #expr, (int) ret); \ 30 exit(1); \ 31 } \ 32} while (0) 33 34void fftw_test () { 35printf ("=== FFTW test ===\n" ); 36int N = 4096 ; 37auto * in = (fftwf_complex * )fftwf_malloc (sizeof (fftwf_complex )* N ); 38auto * out = (fftwf_complex * )fftwf_malloc (sizeof (fftwf_complex )* N ); 39fftwf_plan p = fftwf_plan_dft_1d (N ,in ,out ,FFTW_FORWARD ,FFTW_EXHAUSTIVE ); 40 41const int BATCH_SIZE = 32 * 1024 ; 42const int WARMUP_S = 2 ; 43const int MEASURE_S = 5 ; 44auto t0 = high_resolution_clock::now (); 45while (high_resolution_clock::now ()< t0 + std::chrono::seconds (WARMUP_S )) { 46for (int i = 0 ;i < BATCH_SIZE ;i ++ ) { 47fftwf_execute (p ); 48 } 49 } 50auto t1 = high_resolution_clock::now (); 51int n_iter = 0 ; 52while (high_resolution_clock::now ()< t0 + std::chrono::seconds (WARMUP_S + MEASURE_S )) { 53for (int i = 0 ;i < BATCH_SIZE ;i ++ ) { 54fftwf_execute (p ); 55 } 56n_iter += BATCH_SIZE ; 57 } 58auto t2 = high_resolution_clock::now (); 59 60auto ns_int = duration_cast < std::chrono::nanoseconds > (t2 - t1 ); 61printf ("%d ns\n" ,static_cast < int > (ns_int .count () /n_iter )); 62 63fftwf_destroy_plan (p ); 64fftwf_free (in ); 65fftwf_free (out ); 66} 67 68void vkfft_test () { 69printf ("=== vkFFT test ===\n" ); 70 71VkApplicationInfo ai = {}; 72ai .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO ; 73ai .apiVersion = VK_API_VERSION_1_1 ;// match vkFFT test suite 74 75VkInstanceCreateInfo ici = {}; 76ici .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO ; 77ici .pApplicationInfo = & ai ; 78#ifdef __APPLE__ 79const char * ici_ext []= { 80VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME , 81 }; 82ici .enabledExtensionCount = 1 ; 83ici .ppEnabledExtensionNames = ici_ext ; 84ici .flags |=VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR ; 85#endif // __APPLE__ 86 87VkInstance instance ; 88CHECK (vkCreateInstance (& ici ,nullptr ,& instance )); 89 90uint32_t n_dev = 0 ; 91CHECK (vkEnumeratePhysicalDevices (instance ,& n_dev ,nullptr )); 92CHECK_TRUE (n_dev > 0 ); 93 std::vector < VkPhysicalDevice > devices (n_dev ); 94CHECK (vkEnumeratePhysicalDevices (instance ,& n_dev ,devices .data ())); 95VkPhysicalDevice phys_dev = devices [0 ]; 96 97uint32_t n_fam = 0 ; 98vkGetPhysicalDeviceQueueFamilyProperties (phys_dev ,& n_fam ,nullptr ); 99 std::vector < VkQueueFamilyProperties > families (n_fam ); 100vkGetPhysicalDeviceQueueFamilyProperties (phys_dev ,& n_fam ,families .data ()); 101 102// Find a compute queue. 103uint32_t family ; 104for (family = 0 ;family < n_fam ;family ++ ) { 105if (families [family ].queueFlags & VK_QUEUE_COMPUTE_BIT ) { 106break ; 107 } 108 } 109CHECK_TRUE (family < n_fam ); 110const uint64_t ts_bits = families [family ].timestampValidBits ; 111CHECK_TRUE (ts_bits > 0 ); 112 113float priority = 1 ; 114VkDeviceQueueCreateInfo qci = {}; 115qci .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO ; 116qci .queueFamilyIndex = family ; 117qci .queueCount = 1 ; 118qci .pQueuePriorities = & priority ; 119 120VkDeviceCreateInfo dci = {}; 121dci .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO ; 122dci .queueCreateInfoCount = 1 ; 123dci .pQueueCreateInfos = & qci ; 124 125VkDevice device ; 126CHECK (vkCreateDevice (phys_dev ,& dci ,/*VkAllocationCallbacks*/ nullptr ,& device )); 127 128VkQueue queue ; 129vkGetDeviceQueue (device ,family ,0 ,& queue ); 130 131// Get GPU timestamp query 132VkQueryPoolCreateInfo qi = {}; 133qi .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO ; 134qi .queryType = VK_QUERY_TYPE_TIMESTAMP ; 135qi .queryCount = 2 ; 136 137VkQueryPool queries ; 138CHECK (vkCreateQueryPool (device ,& qi ,nullptr ,& queries )); 139 140VkPhysicalDeviceProperties phys_dev_props ; 141vkGetPhysicalDeviceProperties (phys_dev ,& phys_dev_props ); 142 143VkCommandPoolCreateInfo pci = {}; 144pci .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO ; 145pci .queueFamilyIndex = family ; 146pci .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT ; 147 148VkCommandPool pool ; 149CHECK (vkCreateCommandPool (device ,& pci ,nullptr ,& pool )); 150 151VkFenceCreateInfo fci = {}; 152fci .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO ; 153 154VkFence fence ; 155CHECK (vkCreateFence (device ,& fci ,nullptr ,& fence )); 156 157// Length-4096 complex input. All zeros. 158const uint64_t FFT_SIZE = 4096 ; 159const uint64_t BATCH_SIZE = 1024 * 4 ; 160 std::vector < float > data (2 * FFT_SIZE * BATCH_SIZE ,0 ); 161uint64_t buf_sz = data .size ()* sizeof (data [0 ]); 162 163VkBufferCreateInfo bci = {}; 164bci .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO ; 165bci .size = buf_sz ; 166bci .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT ; 167bci .sharingMode = VK_SHARING_MODE_EXCLUSIVE ; 168 169VkBuffer buffer ; 170CHECK (vkCreateBuffer (device ,& bci ,nullptr ,& buffer )); 171 172VkMemoryRequirements mem_req ; 173vkGetBufferMemoryRequirements (device ,buffer ,& mem_req ); 174 175VkPhysicalDeviceMemoryProperties mem_props ; 176vkGetPhysicalDeviceMemoryProperties (phys_dev ,& mem_props ); 177 178const VkMemoryPropertyFlags wanted = 179VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | 180VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | 181VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ; 182uint32_t mem_type ; 183for (mem_type = 0 ;mem_type < mem_props .memoryTypeCount ;mem_type ++ ) { 184if (!(mem_req .memoryTypeBits & (1u <<mem_type ))) { 185continue ; 186 } 187if ((mem_props .memoryTypes [mem_type ].propertyFlags & wanted )!= wanted ) { 188continue ; 189 } 190break ; 191 } 192CHECK_TRUE (mem_type < mem_props .memoryTypeCount ); 193 194VkMemoryAllocateInfo mai = {}; 195mai .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO ; 196mai .allocationSize = mem_req .size ; 197mai .memoryTypeIndex = mem_type ; 198 199VkDeviceMemory memory ; 200CHECK (vkAllocateMemory (device ,& mai ,nullptr ,& memory )); 201CHECK (vkBindBufferMemory (device ,buffer ,memory ,0 )); 202 203void * mapped ; 204CHECK (vkMapMemory (device ,memory ,0 ,buf_sz ,0 ,& mapped )); 205memcpy (mapped ,data .data (),buf_sz ); 206 207// Finally into vkFFT shit now... 208VkFFTConfiguration config = {}; 209config .FFTdim = 1 ; 210config .size [0 ]= FFT_SIZE ; 211config .numberBatches = BATCH_SIZE ; 212config .device = & device ; 213config .physicalDevice = & phys_dev ; 214config .queue = & queue ; 215config .commandPool = & pool ; 216config .fence = & fence ; 217config .buffer = & buffer ; 218config .bufferSize = & buf_sz ; 219 220VkFFTApplication app = {}; 221CHECK (initializeVkFFT (& app ,config )); 222 223VkCommandBufferAllocateInfo cai = {}; 224cai .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO ; 225cai .commandPool = pool ; 226cai .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY ; 227cai .commandBufferCount = 1 ; 228 229VkCommandBuffer cmd ; 230CHECK (vkAllocateCommandBuffers (device ,& cai ,& cmd )); 231 232VkCommandBufferBeginInfo begin = {}; 233begin .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO ; 234CHECK (vkBeginCommandBuffer (cmd ,& begin )); 235 236vkCmdResetQueryPool (cmd ,queries ,0 ,2 ); 237vkCmdWriteTimestamp (cmd ,VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT ,queries ,0 ); 238 239VkFFTLaunchParams launch = {}; 240launch .commandBuffer = & cmd ; 241CHECK (VkFFTAppend (& app ,-1 ,& launch )); 242 243vkCmdWriteTimestamp (cmd ,VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT ,queries ,1 ); 244 245VkMemoryBarrier barrier = {}; 246barrier .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER ; 247barrier .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT ; 248barrier .dstAccessMask = VK_ACCESS_HOST_READ_BIT ; 249vkCmdPipelineBarrier ( 250cmd , 251VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT , 252VK_PIPELINE_STAGE_HOST_BIT , 2530 ,1 ,& barrier ,0 ,nullptr ,0 ,nullptr ); 254 255CHECK (vkEndCommandBuffer (cmd )); 256 257VkSubmitInfo submit = {}; 258submit .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO ; 259submit .commandBufferCount = 1 ; 260submit .pCommandBuffers = & cmd ; 261CHECK (vkQueueSubmit (queue ,1 ,& submit ,VK_NULL_HANDLE )); 262CHECK (vkQueueWaitIdle (queue )); 263 264uint64_t timestamps [2 ]= {}; 265CHECK (vkGetQueryPoolResults ( 266device ,queries ,0 ,2 , 267sizeof (timestamps ),timestamps ,sizeof (timestamps [0 ]), 268VK_QUERY_RESULT_64_BIT |VK_QUERY_RESULT_WAIT_BIT )); 269 270uint64_t mask = ts_bits == 64 271 ?UINT64_MAX : (UINT64_C (1 ) <<ts_bits )- 1 ; 272uint64_t ticks = (timestamps [1 ]- timestamps [0 ])& mask ; 273double ns = double (ticks )* phys_dev_props .limits .timestampPeriod ; 274 275memcpy (data .data (),mapped ,buf_sz ); 276 277// TODO process result 278printf ("Runtime (GPU): %.0f ns\n" ,ns /BATCH_SIZE ); 279 280deleteVkFFT (& app ); 281vkUnmapMemory (device ,memory ); 282vkDestroyBuffer (device ,buffer ,nullptr ); 283vkFreeMemory (device ,memory ,nullptr ); 284vkDestroyFence (device ,fence ,nullptr ); 285vkDestroyCommandPool (device ,pool ,nullptr ); 286vkDestroyQueryPool (device ,queries ,nullptr ); 287vkDestroyDevice (device ,nullptr ); 288vkDestroyInstance (instance ,nullptr ); 289} 290 291int main () { 292//fftw_test(); 293vkfft_test (); 294} 295