GLSL:pow与整数指数的乘法

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 中运行):

\n
package 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}\n
Run Code Online (Sandbox Code Playgroud)\n

采样器函数运行 9x 512\xc2\xb2 像素 * 1000 次,并且每次评估函数 100 次。

\n

我在我的 RX 580(Gigabyte 8GB)上运行此代码,并收集了以下结果:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
力量#多每秒浮点数*GFlopsFpGFlopsIntGFlopsPow加速
211246第1429章第1447章第324章3.84
32266326922708第651章4.09
422682267926986504.12
532766第972章第974章第973章2.84
632785978第974章第976章2.85
7428301295130312992.18
832783279228099602.90
94283612981301第1302章2.18
10428331291第1302章12982.18
11528581623162916231.76
1242824第1302章129513032.17
13528661628162416261.76
14528691614162316111.78
15628861945年1943年1953年1.48
16428211305130013052.16
17 号528681615162516191.77
18528581620162516241.76
19628901949年1946年1949年1.48
20528711618第1627章16251.77
21628791945年1947年1943年1.48
22628861944年1949年1952年1.48
23729012271226922681.28
24528721621162816241.77
25628861942年1943年1942年1.49
26628801949年1949年1953年1.47
27728912273226322661.28
28628831949年1946年1953年1.48
29729102279228122791.28
3072899第2272章227622771.27
31829062598259525961.12
32528721621162516221.77
33629011953年1942年1949年1.49
34628951948年1939年1944年1.49
35728952274226622681.28
36628811937年1944年1948年1.48
37728942277227022801.27
38729022275226422731.28
39829102602259426031.12
40628771945年1947年1945年1.48
41728922276227722821.27
42728872271第2272章22731.27
43829122599260625991.12
44729102278228422761.28
45829202597260126001.12
46829202600260125901.13
47929252921292629271.00
48628851935年1955年1956年1.47
49729012271227922881.27
50729042281227622781.27
51829192608259426071.12
52729022282227022731.28
53829032598260225981.12
54829182602260226041.12
55929322927292429361.00
56729072284228222811.27
57829202606260426101.12
58829132593259725871.13
59929252923292429201.00
60829302614260626131.12
61929322946294629471.00
62929262935293729470.99
631029583258319232660.91
64629021957年1956年1959年1.48
65729032274226722731.28
66729092277227622861.27
67829082602260625991.12
6872894第2272章227922761.27
69829232597260626061.12
70829102596259926001.12
71929262921292729241.00
72729092283227322731.28
73829092602260225991.12
74829142602260226031.12
75929242925292729331.00
76829042608260226011.12
77929112919291729091.00
78929272921291729351.00
791029293241324632460.90
80729032273227622751.28
81829162596259225891.13
82829132600259725981.12
83929252931292629131.00
84829172598260625971.12
85929202916291829271.00
86929422922294429361.00
871029613254325932680.91
88829342607260826121.12
89929182939293129161.00
90929272928292029241.00
911029403253325232460.91
92929242933292629281.00
931029403259323732510.90
941029283247324732640.90
951129333599359335940.82
96728832282226822691.27
97829112602259526001.12
98828962588259125871.12
99929242939293629381.00
\n
\n

正如您所看到的,一次 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

\n


Ret*_*adi 8

虽然这肯定是硬件/供应商/编译器相关的,但高级数学函数pow()往往比基本操作昂贵得多.

当然,最好的方法是尝试两者和基准测试.但是如果有一个简单的替代高级数学函数,我认为你不会因为使用它而出错.

如果你写pow(x, 3.0),你可能希望的最好的是编译器将识别特殊情况,并展开它.但是,为什么要冒这个风险,如果替换的时间短而易读?C/C++编译器并不总是用pow(x, 2.0)简单的乘法代替,所以我不一定指望所有GLSL编译器都这样做.