Ser*_*gey 2 opengl shader glsl exponent
这在GLSL中更快:
pow(x, 3.0f);
Run Code Online (Sandbox Code Playgroud)
要么
x*x*x;
Run Code Online (Sandbox Code Playgroud)
?
取幂性能是否取决于硬件供应商或指数值?
Ant*_*ack 12
我编写了一个小型基准测试,因为我对结果感兴趣。\n就我个人而言,我对指数 = 5 最感兴趣。
\n基准代码(在 Rem's Studio / LWJGL 中运行):
\npackage me.anno.utils.bench\n\nimport me.anno.gpu.GFX\nimport me.anno.gpu.GFX.flat01\nimport me.anno.gpu.RenderState\nimport me.anno.gpu.RenderState.useFrame\nimport me.anno.gpu.framebuffer.Frame\nimport me.anno.gpu.framebuffer.Framebuffer\nimport me.anno.gpu.hidden.HiddenOpenGLContext\nimport me.anno.gpu.shader.Renderer\nimport me.anno.gpu.shader.Shader\nimport me.anno.utils.types.Floats.f2\nimport org.lwjgl.opengl.GL11.*\nimport java.nio.ByteBuffer\nimport kotlin.math.roundToInt\n\nfun main() {\n\n fun createShader(code: String) = Shader(\n "", null, "" +\n "attribute vec2 attr0;\\n" +\n "void main(){\\n" +\n " gl_Position = vec4(attr0*2.0-1.0, 0.0, 1.0);\\n" +\n " uv = attr0;\\n" +\n "}", "varying vec2 uv;\\n", "" +\n "void main(){" +\n code +\n "}"\n )\n\n fun repeat(code: String, times: Int): String {\n return Array(times) { code }.joinToString("\\n")\n }\n\n val size = 512\n\n val warmup = 50\n val benchmark = 1000\n\n HiddenOpenGLContext.setSize(size, size)\n HiddenOpenGLContext.createOpenGL()\n\n val buffer = Framebuffer("", size, size, 1, 1, true, Framebuffer.DepthBufferType.NONE)\n\n println("Power,Multiplications,GFlops-multiplication,GFlops-floats,GFlops-ints,GFlops-power,Speedup")\n\n useFrame(buffer, Renderer.colorRenderer) {\n\n RenderState.blendMode.use(me.anno.gpu.blending.BlendMode.ADD) {\n\n for (power in 2 until 100) {\n\n // to reduce the overhead of other stuff\n val repeats = 100\n val init = "float x1 = dot(uv, vec2(1.0)),x2,x4,x8,x16,x32,x64;\\n"\n val end = "gl_FragColor = vec4(x1,x1,x1,x1);\\n"\n val manualCode = StringBuilder()\n for (bit in 1 until 32) {\n val p = 1.shl(bit)\n val h = 1.shl(bit - 1)\n if (power == p) {\n manualCode.append("x1=x$h*x$h;")\n break\n } else if (power > p) {\n manualCode.append("x$p=x$h*x$h;")\n } else break\n }\n\n if (power.and(power - 1) != 0) {\n // not a power of two, so the result isn\'t finished yet\n manualCode.append("x1=")\n var first = true\n for (bit in 0 until 32) {\n val p = 1.shl(bit)\n if (power.and(p) != 0) {\n if (!first) {\n manualCode.append(\'*\')\n } else first = false\n manualCode.append("x$p")\n }\n }\n manualCode.append(";\\n")\n }\n\n val multiplications = manualCode.count { it == \'*\' }\n\n // println("$power: $manualCode")\n\n val shaders = listOf(\n // manually optimized\n createShader(init + repeat(manualCode.toString(), repeats) + end),\n // can be optimized\n createShader(init + repeat("x1=pow(x1,$power.0);", repeats) + end),\n // can be optimized, int as power\n createShader(init + repeat("x1=pow(x1,$power);", repeats) + end),\n // slightly different, so it can\'t be optimized\n createShader(init + repeat("x1=pow(x1,${power}.01);", repeats) + end),\n )\n\n for (shader in shaders) {\n shader.use()\n }\n\n val pixels = ByteBuffer.allocateDirect(4)\n\n Frame.bind()\n\n glClearColor(0f, 0f, 0f, 1f)\n glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)\n\n for (i in 0 until warmup) {\n for (shader in shaders) {\n shader.use()\n flat01.draw(shader)\n }\n }\n\n val flops = DoubleArray(shaders.size)\n val avg = 10 // for more stability between runs\n for (j in 0 until avg) {\n for (index in shaders.indices) {\n val shader = shaders[index]\n GFX.check()\n val t0 = System.nanoTime()\n for (i in 0 until benchmark) {\n shader.use()\n flat01.draw(shader)\n }\n // synchronize\n glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels)\n GFX.check()\n val t1 = System.nanoTime()\n // the first one may be an outlier\n if (j > 0) flops[index] += multiplications * repeats.toDouble() * benchmark.toDouble() * size * size / (t1 - t0)\n GFX.check()\n }\n }\n\n for (i in flops.indices) {\n flops[i] /= (avg - 1.0)\n }\n\n println(\n "" +\n "$power,$multiplications," +\n "${flops[0].roundToInt()}," +\n "${flops[1].roundToInt()}," +\n "${flops[2].roundToInt()}," +\n "${flops[3].roundToInt()}," +\n (flops[0] / flops[3]).f2()\n )\n\n }\n }\n }\n\n\n}\nRun Code Online (Sandbox Code Playgroud)\n采样器函数运行 9x 512\xc2\xb2 像素 * 1000 次,并且每次评估函数 100 次。
\n我在我的 RX 580(Gigabyte 8GB)上运行此代码,并收集了以下结果:
\n| 力量 | #多 | 每秒浮点数* | GFlopsFp | GFlopsInt | GFlopsPow | 加速 |
|---|---|---|---|---|---|---|
| 2 | 1 | 1246 | 第1429章 | 第1447章 | 第324章 | 3.84 |
| 3 | 2 | 2663 | 2692 | 2708 | 第651章 | 4.09 |
| 4 | 2 | 2682 | 2679 | 2698 | 650 | 4.12 |
| 5 | 3 | 2766 | 第972章 | 第974章 | 第973章 | 2.84 |
| 6 | 3 | 2785 | 978 | 第974章 | 第976章 | 2.85 |
| 7 | 4 | 2830 | 1295 | 1303 | 1299 | 2.18 |
| 8 | 3 | 2783 | 2792 | 2809 | 960 | 2.90 |
| 9 | 4 | 2836 | 1298 | 1301 | 第1302章 | 2.18 |
| 10 | 4 | 2833 | 1291 | 第1302章 | 1298 | 2.18 |
| 11 | 5 | 2858 | 1623 | 1629 | 1623 | 1.76 |
| 12 | 4 | 2824 | 第1302章 | 1295 | 1303 | 2.17 |
| 13 | 5 | 2866 | 1628 | 1624 | 1626 | 1.76 |
| 14 | 5 | 2869 | 1614 | 1623 | 1611 | 1.78 |
| 15 | 6 | 2886 | 1945年 | 1943年 | 1953年 | 1.48 |
| 16 | 4 | 2821 | 1305 | 1300 | 1305 | 2.16 |
| 17 号 | 5 | 2868 | 1615 | 1625 | 1619 | 1.77 |
| 18 | 5 | 2858 | 1620 | 1625 | 1624 | 1.76 |
| 19 | 6 | 2890 | 1949年 | 1946年 | 1949年 | 1.48 |
| 20 | 5 | 2871 | 1618 | 第1627章 | 1625 | 1.77 |
| 21 | 6 | 2879 | 1945年 | 1947年 | 1943年 | 1.48 |
| 22 | 6 | 2886 | 1944年 | 1949年 | 1952年 | 1.48 |
| 23 | 7 | 2901 | 2271 | 2269 | 2268 | 1.28 |
| 24 | 5 | 2872 | 1621 | 1628 | 1624 | 1.77 |
| 25 | 6 | 2886 | 1942年 | 1943年 | 1942年 | 1.49 |
| 26 | 6 | 2880 | 1949年 | 1949年 | 1953年 | 1.47 |
| 27 | 7 | 2891 | 2273 | 2263 | 2266 | 1.28 |
| 28 | 6 | 2883 | 1949年 | 1946年 | 1953年 | 1.48 |
| 29 | 7 | 2910 | 2279 | 2281 | 2279 | 1.28 |
| 30 | 7 | 2899 | 第2272章 | 2276 | 2277 | 1.27 |
| 31 | 8 | 2906 | 2598 | 2595 | 2596 | 1.12 |
| 32 | 5 | 2872 | 1621 | 1625 | 1622 | 1.77 |
| 33 | 6 | 2901 | 1953年 | 1942年 | 1949年 | 1.49 |
| 34 | 6 | 2895 | 1948年 | 1939年 | 1944年 | 1.49 |
| 35 | 7 | 2895 | 2274 | 2266 | 2268 | 1.28 |
| 36 | 6 | 2881 | 1937年 | 1944年 | 1948年 | 1.48 |
| 37 | 7 | 2894 | 2277 | 2270 | 2280 | 1.27 |
| 38 | 7 | 2902 | 2275 | 2264 | 2273 | 1.28 |
| 39 | 8 | 2910 | 2602 | 2594 | 2603 | 1.12 |
| 40 | 6 | 2877 | 1945年 | 1947年 | 1945年 | 1.48 |
| 41 | 7 | 2892 | 2276 | 2277 | 2282 | 1.27 |
| 42 | 7 | 2887 | 2271 | 第2272章 | 2273 | 1.27 |
| 43 | 8 | 2912 | 2599 | 2606 | 2599 | 1.12 |
| 44 | 7 | 2910 | 2278 | 2284 | 2276 | 1.28 |
| 45 | 8 | 2920 | 2597 | 2601 | 2600 | 1.12 |
| 46 | 8 | 2920 | 2600 | 2601 | 2590 | 1.13 |
| 47 | 9 | 2925 | 2921 | 2926 | 2927 | 1.00 |
| 48 | 6 | 2885 | 1935年 | 1955年 | 1956年 | 1.47 |
| 49 | 7 | 2901 | 2271 | 2279 | 2288 | 1.27 |
| 50 | 7 | 2904 | 2281 | 2276 | 2278 | 1.27 |
| 51 | 8 | 2919 | 2608 | 2594 | 2607 | 1.12 |
| 52 | 7 | 2902 | 2282 | 2270 | 2273 | 1.28 |
| 53 | 8 | 2903 | 2598 | 2602 | 2598 | 1.12 |
| 54 | 8 | 2918 | 2602 | 2602 | 2604 | 1.12 |
| 55 | 9 | 2932 | 2927 | 2924 | 2936 | 1.00 |
| 56 | 7 | 2907 | 2284 | 2282 | 2281 | 1.27 |
| 57 | 8 | 2920 | 2606 | 2604 | 2610 | 1.12 |
| 58 | 8 | 2913 | 2593 | 2597 | 2587 | 1.13 |
| 59 | 9 | 2925 | 2923 | 2924 | 2920 | 1.00 |
| 60 | 8 | 2930 | 2614 | 2606 | 2613 | 1.12 |
| 61 | 9 | 2932 | 2946 | 2946 | 2947 | 1.00 |
| 62 | 9 | 2926 | 2935 | 2937 | 2947 | 0.99 |
| 63 | 10 | 2958 | 3258 | 3192 | 3266 | 0.91 |
| 64 | 6 | 2902 | 1957年 | 1956年 | 1959年 | 1.48 |
| 65 | 7 | 2903 | 2274 | 2267 | 2273 | 1.28 |
| 66 | 7 | 2909 | 2277 | 2276 | 2286 | 1.27 |
| 67 | 8 | 2908 | 2602 | 2606 | 2599 | 1.12 |
| 68 | 7 | 2894 | 第2272章 | 2279 | 2276 | 1.27 |
| 69 | 8 | 2923 | 2597 | 2606 | 2606 | 1.12 |
| 70 | 8 | 2910 | 2596 | 2599 | 2600 | 1.12 |
| 71 | 9 | 2926 | 2921 | 2927 | 2924 | 1.00 |
| 72 | 7 | 2909 | 2283 | 2273 | 2273 | 1.28 |
| 73 | 8 | 2909 | 2602 | 2602 | 2599 | 1.12 |
| 74 | 8 | 2914 | 2602 | 2602 | 2603 | 1.12 |
| 75 | 9 | 2924 | 2925 | 2927 | 2933 | 1.00 |
| 76 | 8 | 2904 | 2608 | 2602 | 2601 | 1.12 |
| 77 | 9 | 2911 | 2919 | 2917 | 2909 | 1.00 |
| 78 | 9 | 2927 | 2921 | 2917 | 2935 | 1.00 |
| 79 | 10 | 2929 | 3241 | 3246 | 3246 | 0.90 |
| 80 | 7 | 2903 | 2273 | 2276 | 2275 | 1.28 |
| 81 | 8 | 2916 | 2596 | 2592 | 2589 | 1.13 |
| 82 | 8 | 2913 | 2600 | 2597 | 2598 | 1.12 |
| 83 | 9 | 2925 | 2931 | 2926 | 2913 | 1.00 |
| 84 | 8 | 2917 | 2598 | 2606 | 2597 | 1.12 |
| 85 | 9 | 2920 | 2916 | 2918 | 2927 | 1.00 |
| 86 | 9 | 2942 | 2922 | 2944 | 2936 | 1.00 |
| 87 | 10 | 2961 | 3254 | 3259 | 3268 | 0.91 |
| 88 | 8 | 2934 | 2607 | 2608 | 2612 | 1.12 |
| 89 | 9 | 2918 | 2939 | 2931 | 2916 | 1.00 |
| 90 | 9 | 2927 | 2928 | 2920 | 2924 | 1.00 |
| 91 | 10 | 2940 | 3253 | 3252 | 3246 | 0.91 |
| 92 | 9 | 2924 | 2933 | 2926 | 2928 | 1.00 |
| 93 | 10 | 2940 | 3259 | 3237 | 3251 | 0.90 |
| 94 | 10 | 2928 | 3247 | 3247 | 3264 | 0.90 |
| 95 | 11 | 2933 | 3599 | 3593 | 3594 | 0.82 |
| 96 | 7 | 2883 | 2282 | 2268 | 2269 | 1.27 |
| 97 | 8 | 2911 | 2602 | 2595 | 2600 | 1.12 |
| 98 | 8 | 2896 | 2588 | 2591 | 2587 | 1.12 |
| 99 | 9 | 2924 | 2939 | 2936 | 2938 | 1.00 |
正如您所看到的,一次 power() 调用正好需要 9 个乘法指令。因此,每次手动重写乘法次数少于 9 次的幂都会更快。
\n我的驱动程序仅优化了情况 2、3、4 和 8。优化与指数是否使用 .0 后缀无关。
\n在指数 = 2 的情况下,我的实现似乎比驱动程序的性能更低。我不确定,为什么。
\n与 pow(x,exponent+0.01) 相比,加速比是手动实现的,无法由编译器优化。
\n由于乘法和加速如此完美地对齐,我创建了一个图表来显示这种关系。这种关系表明我的基准是值得信赖的:)。
\n\n操作系统:Windows 10 个人版
\nGPU:来自 Gigabyte 的 RX 580 8GB
\n处理器:Ryzen 5 2600
\n内存:16 GB DDR4 3200
\nGPU 驱动程序:2021 年 6 月 17 日起的 21.6.1
\nLWJGL:版本 3.2.3 build 13
虽然这肯定是硬件/供应商/编译器相关的,但高级数学函数pow()往往比基本操作昂贵得多.
当然,最好的方法是尝试两者和基准测试.但是如果有一个简单的替代高级数学函数,我认为你不会因为使用它而出错.
如果你写pow(x, 3.0),你可能希望的最好的是编译器将识别特殊情况,并展开它.但是,为什么要冒这个风险,如果替换的时间短而易读?C/C++编译器并不总是用pow(x, 2.0)简单的乘法代替,所以我不一定指望所有GLSL编译器都这样做.