From 17669e5998ff07ff90b95bc714bdfcaf398ed2ea Mon Sep 17 00:00:00 2001 From: ice <1391525377@qq.com> Date: Fri, 1 Aug 2025 11:33:57 +0800 Subject: [PATCH 01/11] feat: init for fast_math inject --- src/gpu.cpp | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/src/gpu.cpp b/src/gpu.cpp index 00a711d09..7d5112bd2 100644 --- a/src/gpu.cpp +++ b/src/gpu.cpp @@ -3524,6 +3524,135 @@ VkShaderModule VulkanDevice::compile_shader_module(const uint32_t* spv_data, siz return shader_module; } +static void inject_fast_math(const uint32_t* code, size_t size, std::vector& dstcode) +{ + // 基本验证 + if (size < 20 || code[0] != 0x07230203) + { + dstcode.assign(code, code + size / sizeof(uint32_t)); + return; + } + + // ========================================================================= + // Pass 1: 分析 SPIR-V,收集所有必需的 ID 和锚点指针 + // ========================================================================= + uint32_t bound = code[3]; + uint32_t entry_point_id = 0; + uint32_t float32_type_id = 0; + uint32_t uint32_type_id = 0; + bool has_float_controls2_capability = false; + bool has_float_controls2_extension = false; + + const uint32_t* memory_model_ptr = nullptr; + const uint32_t* first_function_ptr = nullptr; + + const uint32_t* p = code + 5; + const uint32_t* end = code + (size / sizeof(uint32_t)); + + while (p < end) + { + uint16_t wordcount = p[0] >> 16; + if (wordcount == 0 || p + wordcount > end) break; // 安全检查 + uint16_t op = p[0] & 0xffff; + + switch (op) { + case 14: // OpMemoryModel + if (!memory_model_ptr) memory_model_ptr = p; + break; + case 15: // OpEntryPoint + if (p[1] == 5 /* GLCompute */) entry_point_id = p[2]; + break; + case 21: // OpTypeInt + if (wordcount == 4 && p[2] == 32 && p[3] == 0) uint32_type_id = p[1]; + break; + case 22: // OpTypeFloat + if (wordcount == 3 && p[2] == 32) float32_type_id = p[1]; + break; + case 54: // OpFunction + if (!first_function_ptr) first_function_ptr = p; + break; + case 2: // OpCapability + if (p[1] == 6029 /* FloatControls2 */) has_float_controls2_capability = true; + break; + case 10: // OpExtension + if (strcmp((const char*)&p[1], "SPV_KHR_float_controls2") == 0) has_float_controls2_extension = true; + break; + } + + // 如果找到了第一个函数,后面的内容无需再扫描以寻找锚点 + if (first_function_ptr) break; + + p += wordcount; + } + + // 如果缺少任何关键信息,则无法安全修改,返回原始代码 + if (entry_point_id == 0 || float32_type_id == 0 || uint32_type_id == 0 || !memory_model_ptr || !first_function_ptr) + { + dstcode.assign(code, code + size / sizeof(uint32_t)); + return; + } + + // ========================================================================= + // Pass 2: 使用锚点构建新的 SPIR-V + // ========================================================================= + dstcode.clear(); + dstcode.reserve(size / sizeof(uint32_t) + 20); + + // -- 准备新ID和数据 -- + uint32_t fast_math_constant_id = bound; + uint32_t new_bound = bound + 1; + const uint32_t fast_math_flags = 0x40000 /* AllowTransform */ | 0x20000 /* AllowReassoc */ | 0x10000 /* AllowContract */; + + // -- 写入新的头部,并更新Bound -- + dstcode.insert(dstcode.end(), code, code + 5); + dstcode[3] = new_bound; + + p = code + 5; + while (p < end) + { + uint16_t wordcount = p[0] >> 16; + if (wordcount == 0) break; + + // 在复制第一条 OpFunction 指令之前,注入 OpConstant + if (p == first_function_ptr) { + dstcode.push_back((4u << 16) | 43 /* OpConstant */); + dstcode.push_back(uint32_type_id); + dstcode.push_back(fast_math_constant_id); + dstcode.push_back(fast_math_flags); + } + + // 复制当前指令 + dstcode.insert(dstcode.end(), p, p + wordcount); + + // 在复制了锚点指令之后,注入新指令 + if (p == memory_model_ptr) + { + if (!has_float_controls2_capability) { + dstcode.push_back((2u << 16) | 2 /* OpCapability */); + dstcode.push_back(6029 /* FloatControls2 */); + } + if (!has_float_controls2_extension) { + const char ext_name[] = "SPV_KHR_float_controls2"; + size_t ext_word_count = (sizeof(ext_name) + 3) / 4; + dstcode.push_back(((ext_word_count + 1) << 16) | 10 /* OpExtension */); + std::vector ext_words(ext_word_count, 0); + memcpy(ext_words.data(), ext_name, sizeof(ext_name)); + dstcode.insert(dstcode.end(), ext_words.begin(), ext_words.end()); + } + } + else if ((p[0] & 0xffff) == 15 /* OpEntryPoint */ && p[2] == entry_point_id) + { + dstcode.push_back((5u << 16) | 16 /* OpExecutionMode */); + dstcode.push_back(entry_point_id); + dstcode.push_back(6028 /* FPFastMathDefault */); + dstcode.push_back(float32_type_id); + dstcode.push_back(fast_math_constant_id); + } + + p += wordcount; + } +} + static void inject_local_size_xyz(const uint32_t* code, size_t size, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z, uint32_t* dstcode, size_t* dstsize) { uint32_t local_size_x_id = -1; @@ -3627,6 +3756,23 @@ VkShaderModule VulkanDevice::compile_shader_module(const uint32_t* spv_data, siz size_t spv_data_size_modified = spv_data_size; inject_local_size_xyz(spv_data, spv_data_size, local_size_x, local_size_y, local_size_z, spv_data_modified, &spv_data_size_modified); + std::vector buffer; + inject_fast_math(spv_data_modified, spv_data_size_modified,buffer); + + // export to file + FILE* fp = fopen("shader.spv", "wb"); + if (fp) + { + fwrite(buffer.data(), sizeof(uint32_t), buffer.size(), fp); + fclose(fp); + } + FILE* fp2 = fopen("shader_modified.spv", "wb"); + if (fp2) + { + fwrite(spv_data_modified, sizeof(uint32_t), spv_data_size_modified / sizeof(uint32_t), fp2); + fclose(fp2); + } + VkShaderModule shader_module = compile_shader_module(spv_data_modified, spv_data_size_modified); free(spv_data_modified); From a62c8782c694b5cb7054a0e5e53cb83fc93d530d Mon Sep 17 00:00:00 2001 From: futz12 <56149058+futz12@users.noreply.github.com> Date: Fri, 1 Aug 2025 03:35:44 +0000 Subject: [PATCH 02/11] apply code-format changes --- src/gpu.cpp | 56 ++++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/gpu.cpp b/src/gpu.cpp index 7d5112bd2..79d1f3b38 100644 --- a/src/gpu.cpp +++ b/src/gpu.cpp @@ -3555,28 +3555,29 @@ static void inject_fast_math(const uint32_t* code, size_t size, std::vector end) break; // 安全检查 uint16_t op = p[0] & 0xffff; - switch (op) { - case 14: // OpMemoryModel - if (!memory_model_ptr) memory_model_ptr = p; - break; - case 15: // OpEntryPoint - if (p[1] == 5 /* GLCompute */) entry_point_id = p[2]; - break; - case 21: // OpTypeInt - if (wordcount == 4 && p[2] == 32 && p[3] == 0) uint32_type_id = p[1]; - break; - case 22: // OpTypeFloat - if (wordcount == 3 && p[2] == 32) float32_type_id = p[1]; - break; - case 54: // OpFunction - if (!first_function_ptr) first_function_ptr = p; - break; - case 2: // OpCapability - if (p[1] == 6029 /* FloatControls2 */) has_float_controls2_capability = true; - break; - case 10: // OpExtension - if (strcmp((const char*)&p[1], "SPV_KHR_float_controls2") == 0) has_float_controls2_extension = true; - break; + switch (op) + { + case 14: // OpMemoryModel + if (!memory_model_ptr) memory_model_ptr = p; + break; + case 15: // OpEntryPoint + if (p[1] == 5 /* GLCompute */) entry_point_id = p[2]; + break; + case 21: // OpTypeInt + if (wordcount == 4 && p[2] == 32 && p[3] == 0) uint32_type_id = p[1]; + break; + case 22: // OpTypeFloat + if (wordcount == 3 && p[2] == 32) float32_type_id = p[1]; + break; + case 54: // OpFunction + if (!first_function_ptr) first_function_ptr = p; + break; + case 2: // OpCapability + if (p[1] == 6029 /* FloatControls2 */) has_float_controls2_capability = true; + break; + case 10: // OpExtension + if (strcmp((const char*)&p[1], "SPV_KHR_float_controls2") == 0) has_float_controls2_extension = true; + break; } // 如果找到了第一个函数,后面的内容无需再扫描以寻找锚点 @@ -3614,7 +3615,8 @@ static void inject_fast_math(const uint32_t* code, size_t size, std::vector buffer; - inject_fast_math(spv_data_modified, spv_data_size_modified,buffer); + inject_fast_math(spv_data_modified, spv_data_size_modified, buffer); // export to file FILE* fp = fopen("shader.spv", "wb"); From b618e032867b9b71171f2cf5308e64650791cef6 Mon Sep 17 00:00:00 2001 From: ice <1391525377@qq.com> Date: Fri, 1 Aug 2025 14:27:23 +0800 Subject: [PATCH 03/11] feat: fast math support --- src/gpu.cpp | 64 +++++------- src/gpu.h | 2 +- src/option.h | 18 ++++ src/pipeline.cpp | 4 +- src/pipeline.h | 2 +- src/pipelinecache.cpp | 31 ++++-- src/pipelinecache.h | 2 +- tests/CMakeLists.txt | 1 + tests/test_fast_math.cpp | 220 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 293 insertions(+), 51 deletions(-) create mode 100644 tests/test_fast_math.cpp diff --git a/src/gpu.cpp b/src/gpu.cpp index 79d1f3b38..62247af86 100644 --- a/src/gpu.cpp +++ b/src/gpu.cpp @@ -3524,18 +3524,16 @@ VkShaderModule VulkanDevice::compile_shader_module(const uint32_t* spv_data, siz return shader_module; } -static void inject_fast_math(const uint32_t* code, size_t size, std::vector& dstcode) +static void inject_fast_math(const uint32_t* code, size_t size, std::vector& dstcode, uint32_t fast_math_flag) { - // 基本验证 + // check spv magic number if (size < 20 || code[0] != 0x07230203) { dstcode.assign(code, code + size / sizeof(uint32_t)); return; } - // ========================================================================= - // Pass 1: 分析 SPIR-V,收集所有必需的 ID 和锚点指针 - // ========================================================================= + // analyze spv uint32_t bound = code[3]; uint32_t entry_point_id = 0; uint32_t float32_type_id = 0; @@ -3552,7 +3550,7 @@ static void inject_fast_math(const uint32_t* code, size_t size, std::vector> 16; - if (wordcount == 0 || p + wordcount > end) break; // 安全检查 + if (wordcount == 0 || p + wordcount > end) break; // for safety uint16_t op = p[0] & 0xffff; switch (op) @@ -3572,7 +3570,7 @@ static void inject_fast_math(const uint32_t* code, size_t size, std::vector> 16; if (wordcount == 0) break; - // 在复制第一条 OpFunction 指令之前,注入 OpConstant + // constant need before at first function if (p == first_function_ptr) { dstcode.push_back((4u << 16) | 43 /* OpConstant */); dstcode.push_back(uint32_type_id); dstcode.push_back(fast_math_constant_id); - dstcode.push_back(fast_math_flags); + dstcode.push_back(fast_math_flag); } - // 复制当前指令 + // Pass dstcode.insert(dstcode.end(), p, p + wordcount); - // 在复制了锚点指令之后,注入新指令 + // inject new instructions if (p == memory_model_ptr) { if (!has_float_controls2_capability) { - dstcode.push_back((2u << 16) | 2 /* OpCapability */); + dstcode.push_back((2u << 16) | 17 /* OpCapability */); dstcode.push_back(6029 /* FloatControls2 */); } if (!has_float_controls2_extension) @@ -3754,33 +3749,24 @@ static void inject_local_size_xyz(const uint32_t* code, size_t size, uint32_t lo *dstsize = (unsigned char*)dp - (unsigned char*)dstcode; } -VkShaderModule VulkanDevice::compile_shader_module(const uint32_t* spv_data, size_t spv_data_size, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z) const +VkShaderModule VulkanDevice::compile_shader_module(const uint32_t* spv_data, size_t spv_data_size, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z, uint32_t fast_math_flag) const { uint32_t* spv_data_modified = (uint32_t*)malloc(spv_data_size); size_t spv_data_size_modified = spv_data_size; inject_local_size_xyz(spv_data, spv_data_size, local_size_x, local_size_y, local_size_z, spv_data_modified, &spv_data_size_modified); - std::vector buffer; - inject_fast_math(spv_data_modified, spv_data_size_modified, buffer); - - // export to file - FILE* fp = fopen("shader.spv", "wb"); - if (fp) + VkShaderModule shader_module; + if (fast_math_flag != 0) { - fwrite(buffer.data(), sizeof(uint32_t), buffer.size(), fp); - fclose(fp); - } - FILE* fp2 = fopen("shader_modified.spv", "wb"); - if (fp2) + std::vector buffer; + inject_fast_math(spv_data_modified, spv_data_size_modified, buffer,fast_math_flag); + + shader_module = compile_shader_module(buffer.data(), buffer.size() * sizeof(uint32_t)); + } else { - fwrite(spv_data_modified, sizeof(uint32_t), spv_data_size_modified / sizeof(uint32_t), fp2); - fclose(fp2); + shader_module = compile_shader_module(spv_data_modified, spv_data_size_modified); } - - VkShaderModule shader_module = compile_shader_module(spv_data_modified, spv_data_size_modified); - free(spv_data_modified); - return shader_module; } diff --git a/src/gpu.h b/src/gpu.h index 7863b2e21..5f574d604 100644 --- a/src/gpu.h +++ b/src/gpu.h @@ -414,7 +414,7 @@ public: VkShaderModule compile_shader_module(const uint32_t* spv_data, size_t spv_data_size) const; // with fixed workgroup size - VkShaderModule compile_shader_module(const uint32_t* spv_data, size_t spv_data_size, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z) const; + VkShaderModule compile_shader_module(const uint32_t* spv_data, size_t spv_data_size, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z, uint32_t fast_math_flag) const; // helper for creating pipeline int create_descriptorset_layout(int binding_count, const int* binding_types, VkDescriptorSetLayout* descriptorset_layout) const; diff --git a/src/option.h b/src/option.h index 9548e41c9..2abdff852 100644 --- a/src/option.h +++ b/src/option.h @@ -56,6 +56,24 @@ public: // pipeline cache PipelineCache* pipeline_cache; + + enum VK_FAST_MATH_FLAG + { + // Base + VK_FAST_MATH_FLAG_DISABLE = 0x0, + VK_FAST_MATH_FLAG_NotNaN = 0x1, // Assume parameters and result are not NaN. If this assumption does not hold then the operation returns an undefined value. + VK_FAST_MATH_FLAG_NotInf = 0x2, // Assume parameters and result are not +/- Inf. If this assumption does not hold then the operation returns an undefined value. + VK_FAST_MATH_FLAG_NSZ = 0x4, // Treat the sign of a zero parameter or result as insignificant. + VK_FAST_MATH_FLAG_AllowRecip = 0x8, // Allow the usage of reciprocal rather than perform a division. + VK_FAST_MATH_FLAG_Fast = 0x10, // Allow algebraic transformations according to real-number associative and distributive algebra. This flag implies above; + // FloatControls2 + VK_FAST_MATH_FLAG_AllowContract = 0x10000, // Allows a floating-point operation to be contracted with any operation(s) producing its operands. Rounding steps may be eliminated or may preserve higher bit-depth than the specified types. The instructions producing the operands do not need to be decorated to allow this transformation. + VK_FAST_MATH_FLAG_AllowReassoc = 0x20000, // Allows a floating-point operation to be reordered with any operation(s) producing its operands according to real-number associativity rules. The instructions producing the operands do not need to be decorated to allow this transformation. + VK_FAST_MATH_FLAG_AllowTransform = 0x40000, // Allows a floating-point operation to be transformed with any operation(s) producing its operands according to real-number rules. This is a superset of AllowContract and AllowReassoc and those bits must be set whenever this bit is set. The instructions producing the operands do not need to be decorated to allow this transformation, but note that non-trivial transformations may require multiple instructions to be decorated. + }; + + // vk fast math mode, 0 is disable + int vk_fast_math_flag; #endif // NCNN_VULKAN // the time openmp threads busy-wait for more work before going to sleep diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 60bbcdfdf..99b87a65a 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -216,14 +216,14 @@ void Pipeline::set_local_size_xyz(int w, int h, int c) // NCNN_LOGE("local size = %d %d %d", local_size_x, local_size_y, local_size_z); } -int Pipeline::create(const uint32_t* spv_data, size_t spv_data_size, const std::vector& specializations) +int Pipeline::create(const uint32_t* spv_data, size_t spv_data_size, const std::vector& specializations, uint32_t fast_math_flag) { const PipelineCache* pipeline_cache = vkdev->get_pipeline_cache(); // get from pipeline cache return pipeline_cache->get_pipeline(spv_data, spv_data_size, specializations, d->local_size_x, d->local_size_y, d->local_size_z, d->subgroup_size, &d->shader_module, &d->descriptorset_layout, &d->pipeline_layout, &d->pipeline, &d->descriptor_update_template, - d->shader_info); + d->shader_info,fast_math_flag); } int Pipeline::create(int shader_type_index, const Option& opt, const std::vector& specializations) diff --git a/src/pipeline.h b/src/pipeline.h index 0a3a9ba91..afdcb54cf 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -27,7 +27,7 @@ public: void set_local_size_xyz(int w, int h, int c); void set_subgroup_size(uint32_t subgroup_size); - int create(const uint32_t* spv_data, size_t spv_data_size, const std::vector& specializations); + int create(const uint32_t* spv_data, size_t spv_data_size, const std::vector& specializations, uint32_t fast_math_flag = 0); int create(int shader_type_index, const Option& opt, const std::vector& specializations); diff --git a/src/pipelinecache.cpp b/src/pipelinecache.cpp index 1bd274514..12e4f2da5 100644 --- a/src/pipelinecache.cpp +++ b/src/pipelinecache.cpp @@ -58,7 +58,7 @@ public: struct pipeline_cache_digest { pipeline_cache_digest(const uint32_t* spv_data, size_t spv_data_size, const std::vector& specializations, - uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z, uint32_t subgroup_size); + uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z, uint32_t subgroup_size, uint32_t fast_math_flag = 0); pipeline_cache_digest(int shader_type_index, const Option& opt, const std::vector& specializations, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z, uint32_t subgroup_size); @@ -88,6 +88,8 @@ public: uint32_t subgroup_size; uint32_t specializations_murmur3; uint32_t specializations_fnv1a; + uint32_t fast_math_flag; + uint32_t reserved_0; // for future use }; struct @@ -96,6 +98,7 @@ public: uint64_t d1; uint64_t d2; uint64_t d3; + uint64_t d4; }; }; }; @@ -116,7 +119,7 @@ public: }; PipelineCachePrivate::pipeline_cache_digest::pipeline_cache_digest(const uint32_t* spv_data, size_t spv_data_size, const std::vector& specializations, - uint32_t _local_size_x, uint32_t _local_size_y, uint32_t _local_size_z, uint32_t _subgroup_size) + uint32_t _local_size_x, uint32_t _local_size_y, uint32_t _local_size_z, uint32_t _subgroup_size, uint32_t _fast_math_flag) { spv_data_murmur3 = murmur3_32(spv_data, spv_data_size / 4); @@ -126,6 +129,8 @@ PipelineCachePrivate::pipeline_cache_digest::pipeline_cache_digest(const uint32_ local_size_y = _local_size_y; local_size_z = _local_size_z; subgroup_size = _subgroup_size; + fast_math_flag = _fast_math_flag; + reserved_0 = 0; // for future use // encode specializations const int specialization_count = specializations.size(); @@ -134,7 +139,7 @@ PipelineCachePrivate::pipeline_cache_digest::pipeline_cache_digest(const uint32_ } PipelineCachePrivate::pipeline_cache_digest::pipeline_cache_digest(int _shader_type_index, const Option& opt, const std::vector& specializations, - uint32_t _local_size_x, uint32_t _local_size_y, uint32_t _local_size_z, uint32_t _subgroup_size) + uint32_t _local_size_x, uint32_t _local_size_y, uint32_t _local_size_z, uint32_t _subgroup_size) { shader_type_index = _shader_type_index; @@ -150,6 +155,8 @@ PipelineCachePrivate::pipeline_cache_digest::pipeline_cache_digest(int _shader_t local_size_y = _local_size_y; local_size_z = _local_size_z; subgroup_size = _subgroup_size; + fast_math_flag = opt.vk_fast_math_flag; + reserved_0 = 0; // for future use // encode specializations const int specialization_count = specializations.size(); @@ -227,11 +234,11 @@ int PipelineCache::get_pipeline(const uint32_t* spv_data, size_t spv_data_size, VkPipelineLayout* pipeline_layout, VkPipeline* pipeline, VkDescriptorUpdateTemplateKHR* descriptor_update_template, - ShaderInfo& shader_info) const + ShaderInfo& shader_info, uint32_t fast_math_flag) const { MutexLockGuard lock(d->cache_lock); - PipelineCachePrivate::pipeline_cache_digest key(spv_data, spv_data_size, specializations, local_size_x, local_size_y, local_size_z, subgroup_size); + PipelineCachePrivate::pipeline_cache_digest key(spv_data, spv_data_size, specializations, local_size_x, local_size_y, local_size_z, subgroup_size, fast_math_flag); if (!vkdev->info.bug_corrupted_online_pipeline_cache()) { @@ -266,7 +273,12 @@ int PipelineCache::get_pipeline(const uint32_t* spv_data, size_t spv_data_size, return -1; } - VkShaderModule shader_module = vkdev->compile_shader_module(spv_data, spv_data_size, local_size_x, local_size_y, local_size_z); + if (fast_math_flag != 0 && !vkdev->info.support_VK_KHR_shader_float_controls2()) + { + NCNN_LOGE("fast_math_flag is not supported on this device"); + return -1; + } + VkShaderModule shader_module = vkdev->compile_shader_module(spv_data, spv_data_size, local_size_x, local_size_y, local_size_z, fast_math_flag); if (!shader_module) { NCNN_LOGE("create_shader_module failed"); @@ -351,6 +363,11 @@ int PipelineCache::get_pipeline(int shader_type_index, const Option& opt, const return -1; } + if (opt.vk_fast_math_flag != 0 && !vkdev->info.support_VK_KHR_shader_float_controls2()) + { + NCNN_LOGE("fast_math_flag is not supported on this device"); + return -1; + } ret = new_pipeline(shader_module, shader_info, specializations, subgroup_size, descriptorset_layout, pipeline_layout, pipeline, descriptor_update_template); if (ret != 0) { @@ -403,7 +420,7 @@ int PipelineCache::create_shader_module(int shader_type_index, const Option& opt return -1; } - VkShaderModule shader_module = vkdev->compile_shader_module(spv_data, spv_data_size, local_size_x, local_size_y, local_size_z); + VkShaderModule shader_module = vkdev->compile_shader_module(spv_data, spv_data_size, local_size_x, local_size_y, local_size_z, opt.vk_fast_math_flag); if (!shader_module) { diff --git a/src/pipelinecache.h b/src/pipelinecache.h index b93c0cfd8..3c0b9f7df 100644 --- a/src/pipelinecache.h +++ b/src/pipelinecache.h @@ -31,7 +31,7 @@ public: VkPipelineLayout* pipeline_layout, VkPipeline* pipeline, VkDescriptorUpdateTemplateKHR* descriptor_update_template, - ShaderInfo& shader_info) const; + ShaderInfo& shader_info, uint32_t fast_math_flag = 0) const; int get_pipeline(int shader_type_index, const Option& opt, const std::vector& specializations, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z, uint32_t subgroup_size, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9d5b6517e..d47042ce0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -65,6 +65,7 @@ ncnn_add_test(paramdict) if(NCNN_VULKAN) ncnn_add_test(command) + ncnn_add_test(fast_math) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") diff --git a/tests/test_fast_math.cpp b/tests/test_fast_math.cpp new file mode 100644 index 000000000..a6eb6a2f7 --- /dev/null +++ b/tests/test_fast_math.cpp @@ -0,0 +1,220 @@ +// Copyright 2021 Tencent +// SPDX-License-Identifier: BSD-3-Clause + +#include "datareader.h" +#include "gpu.h" +#include "mat.h" +#include "net.h" +#include "testutil.h" +#include "benchmark.h" // For ncnn::get_current_time() + +#include +#include +#include // For memset + +int device_index = 1; + +// A data reader that provides zero-filled data, useful for loading models without actual weights. +class DataReaderFromEmpty : public ncnn::DataReader +{ +public: + virtual int scan(const char* format, void* p) const + { + (void)format; // unused + (void)p; // unused + return 0; + } + virtual size_t read(void* buf, size_t size) const + { + memset(buf, 0, size); + return size; + } +}; + +static const char* mish25_param = R"delimiter( +7767517 +26 26 +Input in0 0 1 in0 +Mish mish_0 1 1 in0 1 +Mish mish_1 1 1 1 2 +Mish mish_2 1 1 2 3 +Mish mish_3 1 1 3 4 +Mish mish_4 1 1 4 5 +Mish mish_5 1 1 5 6 +Mish mish_6 1 1 6 7 +Mish mish_7 1 1 7 8 +Mish mish_8 1 1 8 9 +Mish mish_9 1 1 9 10 +Mish mish_10 1 1 10 11 +Mish mish_11 1 1 11 12 +Mish mish_12 1 1 12 13 +Mish mish_13 1 1 13 14 +Mish mish_14 1 1 14 15 +Mish mish_15 1 1 15 16 +Mish mish_16 1 1 16 17 +Mish mish_17 1 1 17 18 +Mish mish_18 1 1 18 19 +Mish mish_19 1 1 19 20 +Mish mish_20 1 1 20 21 +Mish mish_21 1 1 21 22 +Mish mish_22 1 1 22 23 +Mish mish_23 1 1 23 24 +Mish mish_24 1 1 24 out0 +)delimiter"; + +// The main test function to compare default vs. fast math performance. +static int test_vulkan_fast_math() +{ + // Define model path based on environment + // Create a random input matrix + ncnn::Mat input = RandomMat(512, 512, 3); + DataReaderFromEmpty dr; + + // ================================================== + // 1. Setup Net with Default Options + // ================================================== + printf("==================================================\n"); + printf(" Testing with Default Vulkan Options \n"); + printf("==================================================\n"); + ncnn::Net net_default; + net_default.opt.use_vulkan_compute = true; + net_default.opt.vulkan_device_index = device_index; + + net_default.load_param_mem(mish25_param); + net_default.load_model(dr); + printf("Default net loaded successfully.\n"); + + // ================================================== + // 2. Setup Net with Fast Math Options + // ================================================== + printf("\n==================================================\n"); + printf(" Testing with Vulkan Fast Math Options \n"); + printf("==================================================\n"); + ncnn::Net net_fast_math; + net_fast_math.opt.use_vulkan_compute = true; + net_fast_math.opt.vk_fast_math_flag = ncnn::Option::VK_FAST_MATH_FLAG_Fast + | ncnn::Option::VK_FAST_MATH_FLAG_AllowContract + | ncnn::Option::VK_FAST_MATH_FLAG_AllowReassoc + | ncnn::Option::VK_FAST_MATH_FLAG_AllowTransform; + net_fast_math.opt.vulkan_device_index = device_index; + + net_fast_math.load_param_mem(mish25_param); + net_fast_math.load_model(dr); + printf("Fast math net loaded successfully.\n"); + + + // ================================================== + // 3. Warm-up Run + // ================================================== + printf("\n==================================================\n"); + printf(" Warming up both networks... \n"); + printf("==================================================\n"); + ncnn::Mat output_default, output_fast_math; + { + ncnn::Extractor ex = net_default.create_extractor(); + ex.input("in0", input); + ex.extract("out0", output_default); + } + { + ncnn::Extractor ex = net_fast_math.create_extractor(); + ex.input("in0", input); + ex.extract("out0", output_fast_math); + } + printf("Warm-up complete.\n"); + + + // ================================================== + // 4. Benchmark Performance + // ================================================== + printf("\n==================================================\n"); + printf(" Benchmarking Performance \n"); + printf("==================================================\n"); + const int loop_count = 10; + double time_default = 0; + double time_fast_math = 0; + + // Benchmark default net + { + double start = ncnn::get_current_time(); + for (int i = 0; i < loop_count; i++) + { + ncnn::Extractor ex = net_default.create_extractor(); + ex.input("in0", input); + ex.extract("out0", output_default); + } + double end = ncnn::get_current_time(); + time_default = (end - start) / loop_count; + printf("Default Net Average Time: %.2f ms\n", time_default); + } + + // Benchmark fast math net + { + double start = ncnn::get_current_time(); + for (int i = 0; i < loop_count; i++) + { + ncnn::Extractor ex = net_fast_math.create_extractor(); + ex.input("in0", input); + ex.extract("out0", output_fast_math); + } + double end = ncnn::get_current_time(); + time_fast_math = (end - start) / loop_count; + printf("Fast Math Net Average Time: %.2f ms\n", time_fast_math); + } + + // ================================================== + // 5. Verification and Summary + // ================================================== + printf("\n==================================================\n"); + printf(" Verification and Summary \n"); + printf("==================================================\n"); + + // Compare results. A larger tolerance is needed due to fast math optimizations. + int ret = CompareMat(output_default, output_fast_math, 0.01f); + printf("Output comparison result (0 means success): %d\n", ret); + if (ret != 0) + { + fprintf(stderr, "Warning: Output mismatch is larger than tolerance. Fast math might be affecting precision significantly.\n"); + } + else + { + printf("Output verification: SUCCESS (within tolerance)\n"); + } + + printf("--------------------------------------------------\n"); + printf("Performance Summary:\n"); + printf(" - Default Net: %.2f ms\n", time_default); + printf(" - Fast Math Net: %.2f ms\n", time_fast_math); + + if (time_default > 0 && time_fast_math > 0) + { + double speedup = (time_default - time_fast_math) / time_default * 100; + printf(" - Speedup: %.2f%%\n", speedup); + } + + printf("\nTest finished.\n"); + return 0; +} + +int main(int argc, char** argv) +{ + if (argc >= 2) + { + device_index = atoi(argv[1]); + } + + + int gpu_count = ncnn::get_gpu_count(); + if (device_index < 0 || device_index >= gpu_count) + { + fprintf(stderr, "Invalid GPU device index %d. The valid range is [0, %d-1]. Using default device 0.\n", device_index, gpu_count); + device_index = 0; + } + + // Set the default device for all ncnn operations. + printf("Using Vulkan Device: %d\n", device_index); + + // Run the performance test. + int ret = test_vulkan_fast_math(); + + return ret; +} From 1a484b3638d5d33149c2e0eb412a2fe2f35add11 Mon Sep 17 00:00:00 2001 From: futz12 <56149058+futz12@users.noreply.github.com> Date: Fri, 1 Aug 2025 06:29:10 +0000 Subject: [PATCH 04/11] apply code-format changes --- src/gpu.cpp | 5 +++-- src/option.h | 12 ++++++------ src/pipeline.cpp | 2 +- src/pipelinecache.cpp | 4 ++-- tests/test_fast_math.cpp | 3 --- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/gpu.cpp b/src/gpu.cpp index 62247af86..f3bb56d03 100644 --- a/src/gpu.cpp +++ b/src/gpu.cpp @@ -3759,10 +3759,11 @@ VkShaderModule VulkanDevice::compile_shader_module(const uint32_t* spv_data, siz if (fast_math_flag != 0) { std::vector buffer; - inject_fast_math(spv_data_modified, spv_data_size_modified, buffer,fast_math_flag); + inject_fast_math(spv_data_modified, spv_data_size_modified, buffer, fast_math_flag); shader_module = compile_shader_module(buffer.data(), buffer.size() * sizeof(uint32_t)); - } else + } + else { shader_module = compile_shader_module(spv_data_modified, spv_data_size_modified); } diff --git a/src/option.h b/src/option.h index 2abdff852..40f421134 100644 --- a/src/option.h +++ b/src/option.h @@ -61,14 +61,14 @@ public: { // Base VK_FAST_MATH_FLAG_DISABLE = 0x0, - VK_FAST_MATH_FLAG_NotNaN = 0x1, // Assume parameters and result are not NaN. If this assumption does not hold then the operation returns an undefined value. - VK_FAST_MATH_FLAG_NotInf = 0x2, // Assume parameters and result are not +/- Inf. If this assumption does not hold then the operation returns an undefined value. - VK_FAST_MATH_FLAG_NSZ = 0x4, // Treat the sign of a zero parameter or result as insignificant. + VK_FAST_MATH_FLAG_NotNaN = 0x1, // Assume parameters and result are not NaN. If this assumption does not hold then the operation returns an undefined value. + VK_FAST_MATH_FLAG_NotInf = 0x2, // Assume parameters and result are not +/- Inf. If this assumption does not hold then the operation returns an undefined value. + VK_FAST_MATH_FLAG_NSZ = 0x4, // Treat the sign of a zero parameter or result as insignificant. VK_FAST_MATH_FLAG_AllowRecip = 0x8, // Allow the usage of reciprocal rather than perform a division. - VK_FAST_MATH_FLAG_Fast = 0x10, // Allow algebraic transformations according to real-number associative and distributive algebra. This flag implies above; + VK_FAST_MATH_FLAG_Fast = 0x10, // Allow algebraic transformations according to real-number associative and distributive algebra. This flag implies above; // FloatControls2 - VK_FAST_MATH_FLAG_AllowContract = 0x10000, // Allows a floating-point operation to be contracted with any operation(s) producing its operands. Rounding steps may be eliminated or may preserve higher bit-depth than the specified types. The instructions producing the operands do not need to be decorated to allow this transformation. - VK_FAST_MATH_FLAG_AllowReassoc = 0x20000, // Allows a floating-point operation to be reordered with any operation(s) producing its operands according to real-number associativity rules. The instructions producing the operands do not need to be decorated to allow this transformation. + VK_FAST_MATH_FLAG_AllowContract = 0x10000, // Allows a floating-point operation to be contracted with any operation(s) producing its operands. Rounding steps may be eliminated or may preserve higher bit-depth than the specified types. The instructions producing the operands do not need to be decorated to allow this transformation. + VK_FAST_MATH_FLAG_AllowReassoc = 0x20000, // Allows a floating-point operation to be reordered with any operation(s) producing its operands according to real-number associativity rules. The instructions producing the operands do not need to be decorated to allow this transformation. VK_FAST_MATH_FLAG_AllowTransform = 0x40000, // Allows a floating-point operation to be transformed with any operation(s) producing its operands according to real-number rules. This is a superset of AllowContract and AllowReassoc and those bits must be set whenever this bit is set. The instructions producing the operands do not need to be decorated to allow this transformation, but note that non-trivial transformations may require multiple instructions to be decorated. }; diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 99b87a65a..cf59a6ecb 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -223,7 +223,7 @@ int Pipeline::create(const uint32_t* spv_data, size_t spv_data_size, const std:: // get from pipeline cache return pipeline_cache->get_pipeline(spv_data, spv_data_size, specializations, d->local_size_x, d->local_size_y, d->local_size_z, d->subgroup_size, &d->shader_module, &d->descriptorset_layout, &d->pipeline_layout, &d->pipeline, &d->descriptor_update_template, - d->shader_info,fast_math_flag); + d->shader_info, fast_math_flag); } int Pipeline::create(int shader_type_index, const Option& opt, const std::vector& specializations) diff --git a/src/pipelinecache.cpp b/src/pipelinecache.cpp index 12e4f2da5..59b4e12d6 100644 --- a/src/pipelinecache.cpp +++ b/src/pipelinecache.cpp @@ -119,7 +119,7 @@ public: }; PipelineCachePrivate::pipeline_cache_digest::pipeline_cache_digest(const uint32_t* spv_data, size_t spv_data_size, const std::vector& specializations, - uint32_t _local_size_x, uint32_t _local_size_y, uint32_t _local_size_z, uint32_t _subgroup_size, uint32_t _fast_math_flag) + uint32_t _local_size_x, uint32_t _local_size_y, uint32_t _local_size_z, uint32_t _subgroup_size, uint32_t _fast_math_flag) { spv_data_murmur3 = murmur3_32(spv_data, spv_data_size / 4); @@ -139,7 +139,7 @@ PipelineCachePrivate::pipeline_cache_digest::pipeline_cache_digest(const uint32_ } PipelineCachePrivate::pipeline_cache_digest::pipeline_cache_digest(int _shader_type_index, const Option& opt, const std::vector& specializations, - uint32_t _local_size_x, uint32_t _local_size_y, uint32_t _local_size_z, uint32_t _subgroup_size) + uint32_t _local_size_x, uint32_t _local_size_y, uint32_t _local_size_z, uint32_t _subgroup_size) { shader_type_index = _shader_type_index; diff --git a/tests/test_fast_math.cpp b/tests/test_fast_math.cpp index a6eb6a2f7..06e754c40 100644 --- a/tests/test_fast_math.cpp +++ b/tests/test_fast_math.cpp @@ -102,7 +102,6 @@ static int test_vulkan_fast_math() net_fast_math.load_model(dr); printf("Fast math net loaded successfully.\n"); - // ================================================== // 3. Warm-up Run // ================================================== @@ -122,7 +121,6 @@ static int test_vulkan_fast_math() } printf("Warm-up complete.\n"); - // ================================================== // 4. Benchmark Performance // ================================================== @@ -202,7 +200,6 @@ int main(int argc, char** argv) device_index = atoi(argv[1]); } - int gpu_count = ncnn::get_gpu_count(); if (device_index < 0 || device_index >= gpu_count) { From 43dc7e1bc9d0b38316bee546b814c0f80b23aefd Mon Sep 17 00:00:00 2001 From: ice <1391525377@qq.com> Date: Fri, 1 Aug 2025 14:35:39 +0800 Subject: [PATCH 05/11] fix: close fp16 in test --- tests/test_fast_math.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_fast_math.cpp b/tests/test_fast_math.cpp index a6eb6a2f7..a224287da 100644 --- a/tests/test_fast_math.cpp +++ b/tests/test_fast_math.cpp @@ -12,7 +12,7 @@ #include #include // For memset -int device_index = 1; +int device_index = 0; // A data reader that provides zero-filled data, useful for loading models without actual weights. class DataReaderFromEmpty : public ncnn::DataReader @@ -79,6 +79,9 @@ static int test_vulkan_fast_math() ncnn::Net net_default; net_default.opt.use_vulkan_compute = true; net_default.opt.vulkan_device_index = device_index; + net_default.opt.use_fp16_arithmetic = false; + net_default.opt.use_fp16_storage = false; + net_default.opt.use_fp16_packed = false; net_default.load_param_mem(mish25_param); net_default.load_model(dr); @@ -97,6 +100,9 @@ static int test_vulkan_fast_math() | ncnn::Option::VK_FAST_MATH_FLAG_AllowReassoc | ncnn::Option::VK_FAST_MATH_FLAG_AllowTransform; net_fast_math.opt.vulkan_device_index = device_index; + net_fast_math.opt.use_fp16_arithmetic = false; + net_fast_math.opt.use_fp16_packed = false; + net_fast_math.opt.use_fp16_storage = false; net_fast_math.load_param_mem(mish25_param); net_fast_math.load_model(dr); From 307aba4bfdff97a267754057dbb2ffa28d08c3ad Mon Sep 17 00:00:00 2001 From: ice <1391525377@qq.com> Date: Fri, 1 Aug 2025 14:49:35 +0800 Subject: [PATCH 06/11] fix: use resnet to eval fastmath pipeline cache operator== --- src/pipelinecache.cpp | 4 +-- tests/test_fast_math.cpp | 59 ++++++++++++---------------------------- 2 files changed, 19 insertions(+), 44 deletions(-) diff --git a/src/pipelinecache.cpp b/src/pipelinecache.cpp index 59b4e12d6..f0fe9dd14 100644 --- a/src/pipelinecache.cpp +++ b/src/pipelinecache.cpp @@ -64,12 +64,12 @@ public: bool operator==(const pipeline_cache_digest& rhs) const { - return d0 == rhs.d0 && d1 == rhs.d1 && d2 == rhs.d2 && d3 == rhs.d3; + return d0 == rhs.d0 && d1 == rhs.d1 && d2 == rhs.d2 && d3 == rhs.d3 && d4 == rhs.d4; } bool operator!=(const pipeline_cache_digest& rhs) const { - return d0 != rhs.d0 || d1 != rhs.d1 || d2 != rhs.d2 || d3 != rhs.d3; + return d0 != rhs.d0 || d1 != rhs.d1 || d2 != rhs.d2 || d3 != rhs.d3 || d4 != rhs.d4; } union diff --git a/tests/test_fast_math.cpp b/tests/test_fast_math.cpp index 481b9638e..083ed398a 100644 --- a/tests/test_fast_math.cpp +++ b/tests/test_fast_math.cpp @@ -12,7 +12,7 @@ #include #include // For memset -int device_index = 0; +int device_index = 1; // A data reader that provides zero-filled data, useful for loading models without actual weights. class DataReaderFromEmpty : public ncnn::DataReader @@ -31,37 +31,6 @@ public: } }; -static const char* mish25_param = R"delimiter( -7767517 -26 26 -Input in0 0 1 in0 -Mish mish_0 1 1 in0 1 -Mish mish_1 1 1 1 2 -Mish mish_2 1 1 2 3 -Mish mish_3 1 1 3 4 -Mish mish_4 1 1 4 5 -Mish mish_5 1 1 5 6 -Mish mish_6 1 1 6 7 -Mish mish_7 1 1 7 8 -Mish mish_8 1 1 8 9 -Mish mish_9 1 1 9 10 -Mish mish_10 1 1 10 11 -Mish mish_11 1 1 11 12 -Mish mish_12 1 1 12 13 -Mish mish_13 1 1 13 14 -Mish mish_14 1 1 14 15 -Mish mish_15 1 1 15 16 -Mish mish_16 1 1 16 17 -Mish mish_17 1 1 17 18 -Mish mish_18 1 1 18 19 -Mish mish_19 1 1 19 20 -Mish mish_20 1 1 20 21 -Mish mish_21 1 1 21 22 -Mish mish_22 1 1 22 23 -Mish mish_23 1 1 23 24 -Mish mish_24 1 1 24 out0 -)delimiter"; - // The main test function to compare default vs. fast math performance. static int test_vulkan_fast_math() { @@ -70,6 +39,12 @@ static int test_vulkan_fast_math() ncnn::Mat input = RandomMat(512, 512, 3); DataReaderFromEmpty dr; +#ifdef __EMSCRIPTEN__ +#define MODEL_DIR "/working" +#else +#define MODEL_DIR "../../benchmark" +#endif + // ================================================== // 1. Setup Net with Default Options // ================================================== @@ -83,7 +58,7 @@ static int test_vulkan_fast_math() net_default.opt.use_fp16_storage = false; net_default.opt.use_fp16_packed = false; - net_default.load_param_mem(mish25_param); + net_default.load_param(MODEL_DIR "/resnet50.param"); net_default.load_model(dr); printf("Default net loaded successfully.\n"); @@ -104,7 +79,7 @@ static int test_vulkan_fast_math() net_fast_math.opt.use_fp16_packed = false; net_fast_math.opt.use_fp16_storage = false; - net_fast_math.load_param_mem(mish25_param); + net_fast_math.load_param(MODEL_DIR "/resnet50.param"); net_fast_math.load_model(dr); printf("Fast math net loaded successfully.\n"); @@ -117,13 +92,13 @@ static int test_vulkan_fast_math() ncnn::Mat output_default, output_fast_math; { ncnn::Extractor ex = net_default.create_extractor(); - ex.input("in0", input); - ex.extract("out0", output_default); + ex.input("data", input); + ex.extract("output", output_default); } { ncnn::Extractor ex = net_fast_math.create_extractor(); - ex.input("in0", input); - ex.extract("out0", output_fast_math); + ex.input("data", input); + ex.extract("output", output_fast_math); } printf("Warm-up complete.\n"); @@ -143,8 +118,8 @@ static int test_vulkan_fast_math() for (int i = 0; i < loop_count; i++) { ncnn::Extractor ex = net_default.create_extractor(); - ex.input("in0", input); - ex.extract("out0", output_default); + ex.input("data", input); + ex.extract("output", output_default); } double end = ncnn::get_current_time(); time_default = (end - start) / loop_count; @@ -157,8 +132,8 @@ static int test_vulkan_fast_math() for (int i = 0; i < loop_count; i++) { ncnn::Extractor ex = net_fast_math.create_extractor(); - ex.input("in0", input); - ex.extract("out0", output_fast_math); + ex.input("data", input); + ex.extract("output", output_fast_math); } double end = ncnn::get_current_time(); time_fast_math = (end - start) / loop_count; From b008d4b53827c3881a9554e3f0ce39f2d5a7ca2d Mon Sep 17 00:00:00 2001 From: ice <1391525377@qq.com> Date: Fri, 1 Aug 2025 14:51:39 +0800 Subject: [PATCH 07/11] fix: fast math test device index --- tests/test_fast_math.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_fast_math.cpp b/tests/test_fast_math.cpp index 083ed398a..97c6a3122 100644 --- a/tests/test_fast_math.cpp +++ b/tests/test_fast_math.cpp @@ -12,7 +12,7 @@ #include #include // For memset -int device_index = 1; +int device_index = 0; // A data reader that provides zero-filled data, useful for loading models without actual weights. class DataReaderFromEmpty : public ncnn::DataReader From a54d550c04b815e12c0e8ff55b7ba4d997ffa91a Mon Sep 17 00:00:00 2001 From: ice <1391525377@qq.com> Date: Fri, 1 Aug 2025 15:39:00 +0800 Subject: [PATCH 08/11] fix: make ci happy --- src/option.cpp | 1 + tests/test_fast_math.cpp | 17 +++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/option.cpp b/src/option.cpp index e239704db..66099b7a2 100644 --- a/src/option.cpp +++ b/src/option.cpp @@ -23,6 +23,7 @@ Option::Option() workspace_vkallocator = 0; staging_vkallocator = 0; pipeline_cache = 0; + vk_fast_math_flag = 0; // default no fast math #endif // NCNN_VULKAN openmp_blocktime = 20; diff --git a/tests/test_fast_math.cpp b/tests/test_fast_math.cpp index 97c6a3122..242c9cdac 100644 --- a/tests/test_fast_math.cpp +++ b/tests/test_fast_math.cpp @@ -58,7 +58,7 @@ static int test_vulkan_fast_math() net_default.opt.use_fp16_storage = false; net_default.opt.use_fp16_packed = false; - net_default.load_param(MODEL_DIR "/resnet50.param"); + net_default.load_param(MODEL_DIR "/vision_transformer.param"); net_default.load_model(dr); printf("Default net loaded successfully.\n"); @@ -79,7 +79,7 @@ static int test_vulkan_fast_math() net_fast_math.opt.use_fp16_packed = false; net_fast_math.opt.use_fp16_storage = false; - net_fast_math.load_param(MODEL_DIR "/resnet50.param"); + net_fast_math.load_param(MODEL_DIR "/vision_transformer.param"); net_fast_math.load_model(dr); printf("Fast math net loaded successfully.\n"); @@ -92,12 +92,12 @@ static int test_vulkan_fast_math() ncnn::Mat output_default, output_fast_math; { ncnn::Extractor ex = net_default.create_extractor(); - ex.input("data", input); + ex.input("input", input); ex.extract("output", output_default); } { ncnn::Extractor ex = net_fast_math.create_extractor(); - ex.input("data", input); + ex.input("input", input); ex.extract("output", output_fast_math); } printf("Warm-up complete.\n"); @@ -118,7 +118,7 @@ static int test_vulkan_fast_math() for (int i = 0; i < loop_count; i++) { ncnn::Extractor ex = net_default.create_extractor(); - ex.input("data", input); + ex.input("input", input); ex.extract("output", output_default); } double end = ncnn::get_current_time(); @@ -132,7 +132,7 @@ static int test_vulkan_fast_math() for (int i = 0; i < loop_count; i++) { ncnn::Extractor ex = net_fast_math.create_extractor(); - ex.input("data", input); + ex.input("input", input); ex.extract("output", output_fast_math); } double end = ncnn::get_current_time(); @@ -187,6 +187,11 @@ int main(int argc, char** argv) fprintf(stderr, "Invalid GPU device index %d. The valid range is [0, %d-1]. Using default device 0.\n", device_index, gpu_count); device_index = 0; } + if (!ncnn::get_gpu_device(device_index)->info.support_VK_KHR_shader_float_controls2()) + { + fprintf(stderr, "The selected device does not support VK_KHR_shader_float_controls2. Fast math tests may not be valid.\n"); + return 0; + } // Set the default device for all ncnn operations. printf("Using Vulkan Device: %d\n", device_index); From ecee7817796f1ecb16fb4e1ba3cb2a854b958503 Mon Sep 17 00:00:00 2001 From: futz12 <56149058+futz12@users.noreply.github.com> Date: Fri, 1 Aug 2025 07:40:50 +0000 Subject: [PATCH 09/11] apply code-format changes --- src/option.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/option.cpp b/src/option.cpp index 66099b7a2..5811f1ea5 100644 --- a/src/option.cpp +++ b/src/option.cpp @@ -24,7 +24,7 @@ Option::Option() staging_vkallocator = 0; pipeline_cache = 0; vk_fast_math_flag = 0; // default no fast math -#endif // NCNN_VULKAN +#endif // NCNN_VULKAN openmp_blocktime = 20; From 2e7b13bd5753caee4a8b8158fcba2c67a314f440 Mon Sep 17 00:00:00 2001 From: ice <1391525377@qq.com> Date: Fri, 1 Aug 2025 22:10:16 +0800 Subject: [PATCH 10/11] fix: missing fast_math_flag in the Android code --- src/gpu.h | 2 +- src/pipeline.cpp | 2 +- tests/test_fast_math.cpp | 20 +++++++++----------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/gpu.h b/src/gpu.h index 5f574d604..8cd577867 100644 --- a/src/gpu.h +++ b/src/gpu.h @@ -414,7 +414,7 @@ public: VkShaderModule compile_shader_module(const uint32_t* spv_data, size_t spv_data_size) const; // with fixed workgroup size - VkShaderModule compile_shader_module(const uint32_t* spv_data, size_t spv_data_size, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z, uint32_t fast_math_flag) const; + VkShaderModule compile_shader_module(const uint32_t* spv_data, size_t spv_data_size, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z, uint32_t fast_math_flag = 0) const; // helper for creating pipeline int create_descriptorset_layout(int binding_count, const int* binding_types, VkDescriptorSetLayout* descriptorset_layout) const; diff --git a/src/pipeline.cpp b/src/pipeline.cpp index cf59a6ecb..5db1841d4 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -461,7 +461,7 @@ int ImportAndroidHardwareBufferPipeline::create_shader_module(const Option& opt) set_shader_info(shader_info); - VkShaderModule shader_module = vkdev->compile_shader_module(spv_data, spv_data_size, local_size_x(), local_size_y(), local_size_z()); + VkShaderModule shader_module = vkdev->compile_shader_module(spv_data, spv_data_size, local_size_x(), local_size_y(), local_size_z(), opt.fast_math_flag); set_shader_module(shader_module); return 0; diff --git a/tests/test_fast_math.cpp b/tests/test_fast_math.cpp index 242c9cdac..72de752dc 100644 --- a/tests/test_fast_math.cpp +++ b/tests/test_fast_math.cpp @@ -36,7 +36,7 @@ static int test_vulkan_fast_math() { // Define model path based on environment // Create a random input matrix - ncnn::Mat input = RandomMat(512, 512, 3); + ncnn::Mat input = RandomMat(224, 224, 3); DataReaderFromEmpty dr; #ifdef __EMSCRIPTEN__ @@ -58,7 +58,7 @@ static int test_vulkan_fast_math() net_default.opt.use_fp16_storage = false; net_default.opt.use_fp16_packed = false; - net_default.load_param(MODEL_DIR "/vision_transformer.param"); + net_default.load_param(MODEL_DIR "/resnet50.param"); net_default.load_model(dr); printf("Default net loaded successfully.\n"); @@ -70,16 +70,14 @@ static int test_vulkan_fast_math() printf("==================================================\n"); ncnn::Net net_fast_math; net_fast_math.opt.use_vulkan_compute = true; - net_fast_math.opt.vk_fast_math_flag = ncnn::Option::VK_FAST_MATH_FLAG_Fast - | ncnn::Option::VK_FAST_MATH_FLAG_AllowContract - | ncnn::Option::VK_FAST_MATH_FLAG_AllowReassoc - | ncnn::Option::VK_FAST_MATH_FLAG_AllowTransform; + net_fast_math.opt.vk_fast_math_flag = ncnn::Option::VK_FAST_MATH_FLAG_AllowContract; + net_fast_math.opt.vulkan_device_index = device_index; net_fast_math.opt.use_fp16_arithmetic = false; net_fast_math.opt.use_fp16_packed = false; net_fast_math.opt.use_fp16_storage = false; - net_fast_math.load_param(MODEL_DIR "/vision_transformer.param"); + net_fast_math.load_param(MODEL_DIR "/resnet50.param"); net_fast_math.load_model(dr); printf("Fast math net loaded successfully.\n"); @@ -92,12 +90,12 @@ static int test_vulkan_fast_math() ncnn::Mat output_default, output_fast_math; { ncnn::Extractor ex = net_default.create_extractor(); - ex.input("input", input); + ex.input("data", input); ex.extract("output", output_default); } { ncnn::Extractor ex = net_fast_math.create_extractor(); - ex.input("input", input); + ex.input("data", input); ex.extract("output", output_fast_math); } printf("Warm-up complete.\n"); @@ -118,7 +116,7 @@ static int test_vulkan_fast_math() for (int i = 0; i < loop_count; i++) { ncnn::Extractor ex = net_default.create_extractor(); - ex.input("input", input); + ex.input("data", input); ex.extract("output", output_default); } double end = ncnn::get_current_time(); @@ -132,7 +130,7 @@ static int test_vulkan_fast_math() for (int i = 0; i < loop_count; i++) { ncnn::Extractor ex = net_fast_math.create_extractor(); - ex.input("input", input); + ex.input("data", input); ex.extract("output", output_fast_math); } double end = ncnn::get_current_time(); From a16024616714c0d47e9bcbda082a91f21d92c8fd Mon Sep 17 00:00:00 2001 From: ice <1391525377@qq.com> Date: Fri, 1 Aug 2025 22:10:16 +0800 Subject: [PATCH 11/11] fix: missing fast_math_flag in the Android code --- src/gpu.h | 2 +- src/pipeline.cpp | 2 +- tests/test_fast_math.cpp | 20 +++++++++----------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/gpu.h b/src/gpu.h index 5f574d604..8cd577867 100644 --- a/src/gpu.h +++ b/src/gpu.h @@ -414,7 +414,7 @@ public: VkShaderModule compile_shader_module(const uint32_t* spv_data, size_t spv_data_size) const; // with fixed workgroup size - VkShaderModule compile_shader_module(const uint32_t* spv_data, size_t spv_data_size, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z, uint32_t fast_math_flag) const; + VkShaderModule compile_shader_module(const uint32_t* spv_data, size_t spv_data_size, uint32_t local_size_x, uint32_t local_size_y, uint32_t local_size_z, uint32_t fast_math_flag = 0) const; // helper for creating pipeline int create_descriptorset_layout(int binding_count, const int* binding_types, VkDescriptorSetLayout* descriptorset_layout) const; diff --git a/src/pipeline.cpp b/src/pipeline.cpp index cf59a6ecb..ccab71e56 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -461,7 +461,7 @@ int ImportAndroidHardwareBufferPipeline::create_shader_module(const Option& opt) set_shader_info(shader_info); - VkShaderModule shader_module = vkdev->compile_shader_module(spv_data, spv_data_size, local_size_x(), local_size_y(), local_size_z()); + VkShaderModule shader_module = vkdev->compile_shader_module(spv_data, spv_data_size, local_size_x(), local_size_y(), local_size_z(), opt.vk_fast_math_flag); set_shader_module(shader_module); return 0; diff --git a/tests/test_fast_math.cpp b/tests/test_fast_math.cpp index 242c9cdac..72de752dc 100644 --- a/tests/test_fast_math.cpp +++ b/tests/test_fast_math.cpp @@ -36,7 +36,7 @@ static int test_vulkan_fast_math() { // Define model path based on environment // Create a random input matrix - ncnn::Mat input = RandomMat(512, 512, 3); + ncnn::Mat input = RandomMat(224, 224, 3); DataReaderFromEmpty dr; #ifdef __EMSCRIPTEN__ @@ -58,7 +58,7 @@ static int test_vulkan_fast_math() net_default.opt.use_fp16_storage = false; net_default.opt.use_fp16_packed = false; - net_default.load_param(MODEL_DIR "/vision_transformer.param"); + net_default.load_param(MODEL_DIR "/resnet50.param"); net_default.load_model(dr); printf("Default net loaded successfully.\n"); @@ -70,16 +70,14 @@ static int test_vulkan_fast_math() printf("==================================================\n"); ncnn::Net net_fast_math; net_fast_math.opt.use_vulkan_compute = true; - net_fast_math.opt.vk_fast_math_flag = ncnn::Option::VK_FAST_MATH_FLAG_Fast - | ncnn::Option::VK_FAST_MATH_FLAG_AllowContract - | ncnn::Option::VK_FAST_MATH_FLAG_AllowReassoc - | ncnn::Option::VK_FAST_MATH_FLAG_AllowTransform; + net_fast_math.opt.vk_fast_math_flag = ncnn::Option::VK_FAST_MATH_FLAG_AllowContract; + net_fast_math.opt.vulkan_device_index = device_index; net_fast_math.opt.use_fp16_arithmetic = false; net_fast_math.opt.use_fp16_packed = false; net_fast_math.opt.use_fp16_storage = false; - net_fast_math.load_param(MODEL_DIR "/vision_transformer.param"); + net_fast_math.load_param(MODEL_DIR "/resnet50.param"); net_fast_math.load_model(dr); printf("Fast math net loaded successfully.\n"); @@ -92,12 +90,12 @@ static int test_vulkan_fast_math() ncnn::Mat output_default, output_fast_math; { ncnn::Extractor ex = net_default.create_extractor(); - ex.input("input", input); + ex.input("data", input); ex.extract("output", output_default); } { ncnn::Extractor ex = net_fast_math.create_extractor(); - ex.input("input", input); + ex.input("data", input); ex.extract("output", output_fast_math); } printf("Warm-up complete.\n"); @@ -118,7 +116,7 @@ static int test_vulkan_fast_math() for (int i = 0; i < loop_count; i++) { ncnn::Extractor ex = net_default.create_extractor(); - ex.input("input", input); + ex.input("data", input); ex.extract("output", output_default); } double end = ncnn::get_current_time(); @@ -132,7 +130,7 @@ static int test_vulkan_fast_math() for (int i = 0; i < loop_count; i++) { ncnn::Extractor ex = net_fast_math.create_extractor(); - ex.input("input", input); + ex.input("data", input); ex.extract("output", output_fast_math); } double end = ncnn::get_current_time();