How to Avoid Branching on the GPU 如何在GPU避免分支

在GPU避免分支的方法

Authored by Brandon Fogerty(XR Graphics Engineer at Unity Technologies.) with Additional Organized by JP.Lee(李正彪)[email protected]
在这里插入图片描述

概要

这边来稿文章中,希望了解如何编写GPU着色器友好型代码,以避免和分支相关的性能费用。

"分支"有着什么意义?
使用明确的 if / then随时都可以产生分支。编译器遇到条件会作出命令。GPU可去的地方有两处。
因此需要决定要使用哪种代码路径。以下示例展示了在汇编GPU里面运行的示例。
运算变量设置为幻数7,则添加该数字。否则的话要减掉。

在这里插入图片描述

为什么要避免分支?

为了增加CPU使用率,大多数计算机都尝试在管线上执行尽可能多的任务。汇编指令按顺序执行,CPU会尝试在尽可能多的CPU内核上执行尽可能多的指令。举个简单的例子。
想象一下,在Twilight Zone内,我们就是世界上最高效的程序员!
用两行代码写了电子游戏!
CPU会尽可能在每一个CPU内核上各执行一个,共计两个光荣的指令。
这是计算机能够更有效的运行代码。
但是在分支时,计算机可能会花时间准备未运行的代码结果。从结论上看处理器时间被浪费,影响了游戏或应用程序的应答能力。

CPU 侧分枝误诊通常会导致周期遗漏超过40次。– Charles Sanglimsuwan (集成开发人员相关工程师)

运气不错,最新的CPU处理器速度惊人,实际分支预测出色,因此分支错失几乎不会有问题。

但是GPU仍然存在性能问题。

GPU尝试并行解决大量计算,因此大部分GPU不支持分支预测

GPU为什么会发生性能问题?

GPU为生成美丽的图像,喜欢并行进行很多工作!
GPU为进行多种固有的结果计算,进行了精心设计,解决了通过单一滤镜(例如:着色器程序)实施的多种输入和相关的问题。
这就是渲染经常使用GPU(Graphics processor units)的原因。通过几个Shader程序运行位置,法线,贴图坐标等具有不同属性的固有顶点,该Shader程序输出在画面上表现的大量固有像素颜色。
举个例子,显示器是1080 × 960这样的一般HD像素的情况下,GPU在一组输入时,将计算1080 × 960 = 1,036,800个的固有像素颜色值!
那是很多的计算结果!
想象一下在1080x960的高清游戏体验。游戏尝试每秒渲染60个固有的图像或帧。因此GPU在1秒之内需要计算1080x960x60以上的固有像素颜色值!
结论上看一秒之内可以计算62,208,000个固有像素值!

撰写本文的节点,NVDIA最现金的GPU是GeForce RTX 2080 Ti。它有着比英特尔最先进的CPU处理器i9-7980XE更多4,352个Cuda内核,18个内核。
非常大的差异吧?
GPU有大量的处理内核,但是与最新的CPU相比,其核心处理数量仍不足百万。
至少目前还没有!显卡在物理上太大了,可能会很昂贵。
因此,GPU将尝试对要解决的问题类型进行特定假设。GPU充分利用SIMD。SIMD表示单个命令的多种数据。SIMD允许以并行方式为多个输入运行计算。SIMD通常希望输入和输出位于相邻的内存块中。因此SIMD操作不必加载每个输入并单独保存每个结果,而是加载输入并将结果保存为单个加载/保存操作,从而减少昂贵的内存加载和保存。SIMD的使用要求内存布局严格,并在应用程序设计中维持更好地内存缓存一致性上有积极作用。SIMD在CPU和GPU均可使用.
在CPU使用SIMD的示例可参考此处。与CPU程序不同,GPU程序几乎始终使用SIMD。为了充分利用SIMD,GPU通常每个内核都有很多ALU。ALU是算术逻辑设备的缩写。ALU执行数学命令。举个例子,使用“add 4, 3”命令计算结果7。因此,单一内核上有8个ALU的话,就可以同事并行运行8个计算。即,单一GPU内核有8个ALU,则单个GPU内核则可以同事计算8个像素值!但是,着色器阶段使用动态分支会发生什么呢?核心是必须执行所有代码路径,并最终丢弃不满足条件的代码路径。这意味着ALU会浪费时间执行任何未使用的操作。也就是说渲染图像需要更长的时间。

在这里插入图片描述

使用示例:

在Unity里,使用立体shader以保证双眼能够准确渲染。左眼以绿色表示渲染,右眼渲染的显示为红色。下面是使用立体shader渲染双眼锁渲染的单个球体。

针对此的纯接近方式是基于unity_StereoEyeIndex变数的条件方法。

Shader "XR/StereoEyeIndexColor"
{
   
    Properties
    {
   
        _LeftEyeColor("Left Eye Color", COLOR) = (0,1,0,1)
        _RightEyeColor("Right Eye Color", COLOR) = (1,0,0,1)
    }

    SubShader
    {
   
        Tags {
    "RenderType" = "Opaque" }

        Pass
        {
   
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            float4 _LeftEyeColor;
            float4 _RightEyeColor;

            #include "UnityCG.cginc"

            struct appdata
            {
   
                float4 vertex : POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
   
                float4 vertex : SV_POSITION;
                UNITY_VERTEX_OUTPUT_STEREO
            };

            v2f vert (appdata v)
            {
   
                v2f o;

                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_OUTPUT(v2f, o);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

                o.vertex = UnityObjectToClipPos(v.vertex);

                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
   
                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
                if(unity_StereoEyeIndex == 
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值