2021-09-26

ISPC简要介绍

What is ISPC

  • 编译器

    基于LLVM

  • 类c语言

    SIMD code

  • CPU shader

Why ISPC

  • 可以利用simd的特性来提高c/cpp程序的性能
  • 更容易编写、维护
  • 同一个程序可以编译成多个指令集
  • 更容易整合进现有项目

How to use ISPC

  1. 安装编译器(https://ispc.github.io/downloads.html)

  2. 编译

    simple.ispc示例代码

    export void simple(uniform float vin[], uniform float vout[],
                       uniform int count) {
        foreach (index = 0 ... count) {
            float v = vin[index];
            if (v < 3.)
                v = v * v;
            else
                v = sqrt(v);
            vout[index] = v;
        }
    }
    

    编译指令(https://ispc.github.io/ispc.html#Using-The-ISPC-Compiler)

    ispc simple.ispc -O2 -o simple.obj -h simple.h --opt=fast-math --arch=x86-64 --target=sse2
    

    编译后会生成simple.obj和simple.h文件

    #ifdef __cplusplus
    extern "C" {
    #endif // __cplusplus
        extern void simple(float vin[], float vout[], int32_t count);
    #ifdef __cplusplus
    }
    #endif // __cplusplus
    

    C++示例代码

    #include <stdio.h>
    #include "simple.h"
    
    int main() {
        float vin[16], vout[16];
        for (int i = 0; i < 16; ++i)
            vin[i] = i;
    
        simple(vin, vout, 16);
    
        for (int i = 0; i < 16; ++i)
            printf("%d: simple(%f) = %f\n", i, vin[i], vout[i]);
    }
    

ISPC工程编译流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VANScoz4-1632655362956)(image-20210926170950585.png)]

ISPC Programming
  • Uniform
    • Scalar Data
      • Results in a scalar register (eax, ebx, ecx, etc…)
    • 所有SIMD lanes共享同一个变量
  • varing
    • Vector data
      • Results in a SIMD vector register (XMM, YMM, ZMM, etc.)
    • 数据的默认类型
    • 每个simd lane拥有一个独立数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KizfLZkM-1632655362958)(uniform and varing.png)]

  • Built in variables

    • programCount
    • programIndex
  • Control Flow

    • c/c++

      • Conditionals
        • if, else, switch
      • Loops
        • for, while, do…while
    • ispc自定义的

      • foreach, foreach_active, foreach_tiled, foreach_unique

      • cif, cwhile, cdo, cfor

  • Arrays

  • Structures

  • Pointer

  • Memory Allocations

The ISPC Standard Library
  • Logical operators
  • Bit ops
  • Math
  • Clamping and Saturated Arithmetic
  • Transcendental Operations
  • RNG (Not the fastest!)
  • Mask/Cross-lane Operations
  • Reductions
Other Feactures
  • AoS to SoA Helper Functions
  • C++ Like References
  • Binary Operator Overloading for Structures (+, -, *, /, <<, >>)
  • Built-in Task System

The ISPC Parallel Execution Model

概念
  • Program Instance

  • Gang

    一组同时运行的程序实例,数量不超过SIMD位宽的2-4倍

  • program counter

    gang中所有程序实例共享,指向下一条需要执行的指令

  • Execution Mask

    程序实例独有,决定当前执行语句的结果是否作用于当前程序实例

    从C/C++代码中调用ISPC函数时,程序执行模式从APP串行模式转换成了ISPC SPMD模式,这个过程中没有线程创建,也没有隐式的上下文转换。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KD6Y1iXB-1632655362960)(ExecutionMode.png)]

    在ISPC函数中,如果程序实例想要执行某一条语句,那么program counter就会指向这条语句,也就是说,program counter执行的是程序实例执行语句的并集。这就会导致一个问题,因为程序实例根据自身条件语句的不同会执行不同的语句,如果program counter指示cpu执行了程序实例本身所不需要执行的语句,程序实例计算的结果就会出错。这个时候Execution Mask就起作用了,如果某个程序实例不需要某条语句执行的结果,则会将Execution Mask置为off,这样语句执行结果就不会作用到当前程序实例上,对结果就没有影响了。但是这样的情况越多,那么对SIMD的利用率就越低,程序效率就会降低。

    SPMD on SIMD

    每个Progam Instance对应一个SIMD lane

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X5QbAZ9B-1632655362962)(spmd ond simd.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kRNpgQ2u-1632655362963)(cudasimd.png)]

Control Flow Example: If Statements
float x = ..., y = ...;
bool test = (x < y);
mask originalMask = get_current_mask();
set_mask(originalMask & test);
if (any_mask_entries_are_enabled()) {
  // true statements
}
set_mask(originalMask & ~test);
if (any_mask_entries_are_enabled()) {
  // false statements
}
set_mask(originalMask);

上述伪代码很好的说明了Execution Mask的作用,程序计数器会指示cpu执行每一条语句,但当且仅当某个程序实例的mask在enabled的状态下,语句执行结果才能作用到当前程序实例上。还要说明的是,如果所有程序实例mask对于某条语句都为off,则CPU不会执行到这条语句。

Control Flow Example: Loops
int limit = ...;
for (int i = 0; i < limit; ++i) {
    ...
}

​ 如上代码,对于每个程序实例来说,limit的值可能都不一样,这意味有的程序实例循环次数多,有的程序实例循环次数少。程序计数器会拿最大循环次数来执行这个循环体,当某个程序实例所需要的循环次数已经执行完毕时,剩下的循环中,它的mask将被置为off,以消除多余循环次数对该程序实例的影响。

​ 对continue和break的处理有两种方式,一种是当程序实例执行到continue时将Execution Mask置为false,然后接着执行剩下的语句。另一种是如果所有程序在continue后都被禁用,则直接跳过剩下的循环体。

Gang Convergence Guarantees

由于program counter是gang中所有程序实例共享的,故提供了一种收敛保证,即所有执行相同的路径的程序实例都会被同时执行,如果两个程序实例执行不同的控制流路径,则它们会尽快收敛

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vpRk0m69-1632655362964)(image-20210924112121963.png)]

if (programIndex == 0) {
    while (true)  // infinite loop
        ;
}
print("hello, world\n");

由于这种模型,print语句将永远不会被执行。

Uniform变量
  • 减小数据占用空间,降低对带宽的要求,减小对向量寄存器的要求
  • 对控制流做简化
  • 可以用向量处理指令
Uniform Control Flow

如果一个条件语句是基于Uniform变量作判断的,编译器就可以判定所有的程序实例都将执行相同的分支,节省了处理控制流分离,处理mask的开销

Data Races Within a Gang

side-effect:表达式求值过程中是否会改变引用变量的值,如果会改变,则有副作用,若未改变,则不具有副作用

表达式:求值,副作用。

sequence point:表达式的求值和副作用都已经结束。

  • 表达式结尾
  • 函数第一条语句执行之前,此时形参都被赋值了
  • 函数返回,调用者代码执行之前
  • 初始化表达式结尾

当前程序实例执行任何语句的副作用都会在下个序列点之后对gang中其他程序实例可见。


Support For SOA Layout

避免了gather,scatter等某些CPU不支持的操作,转变为Vector loads

struct Foo { float x, y, z; };
uniform Foo a[...] = { ... };
int index = ...;
float x = a[index].x;
struct Foo4 { float x[4], y[4], z[4]; };
uniform Foo4 a[...] = { ... };
int index = ...;
float x = a[index/4].x[index & 3]
struct Foo { float x, y, z; };
soa<4> struct Foo a[...] = { ... };
int index = ...;
float x = a[index].x;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EfmCEea2-1632655362964)(SOA.png)]

Tasking Model

launch

用launch调用的方法跟调用者异步执行,它可能会立即执行或者它会同步在其他核上执行

一个方法调用的多个函数执行没有先后顺序,且它们可能会同步执行。

sync关键词会让调用者等待所有它launch的方法返回后才执行下面的语句


ISPC in Unreal

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值