前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场

SIMD

作者头像
小小杰啊
发布于 2022-12-21 13:04:26
发布于 2022-12-21 13:04:26
74500
代码可运行
举报
文章被收录于专栏:Dimples开发记Dimples开发记
运行总次数:0
代码可运行

# SIMD

# 概述

SIMD(发音/sim-dee/)是“Single Instruction/Multiple Data”的缩写,意为“单指令,多数据”。它是 JavaScript 操作 CPU 对应指令的接口,你可以看做这是一种不同的运算执行模式。与它相对的是 SISD(“Single Instruction/Single Data”),即“单指令,单数据”。 SIMD 的含义是使用一个指令,完成多个数据的运算;SISD 的含义是使用一个指令,完成单个数据的运算,这是 JavaScript 的默认运算模式。显而易见,SIMD 的执行效率要高于 SISD,所以被广泛用于 3D 图形运算、物理模拟等运算量超大的项目之中。

为了理解 SIMD,请看下面的例子。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = [1, 2, 3, 4];
var b = [5, 6, 7, 8];
var c = [];

c[0] = a[0] + b[0];
c[1] = a[1] + b[1];
c[2] = a[2] + b[2];
c[3] = a[3] + b[3];
c // Array[6, 8, 10, 12]

上面代码中,数组ab的对应成员相加,结果放入数组c。它的运算模式是依次处理每个数组成员,一共有四个数组成员,所以需要运算 4 次。

如果采用 SIMD 模式,只要运算一次就够了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1, 2, 3, 4);
var b = SIMD.Float32x4(5, 6, 7, 8);
var c = SIMD.Float32x4.add(a, b); // Float32x4[6, 8, 10, 12]

上面代码之中,数组ab的四个成员的各自相加,只用一条指令就完成了。因此,速度比上一种写法提高了 4 倍。

一次 SIMD 运算,可以处理多个数据,这些数据被称为“通道”(lane)。上面代码中,一次运算了四个数据,因此就是四个通道。

SIMD 通常用于矢量运算。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
v + w = 〈v1,, vn〉+ 〈w1,, wn〉
      = 〈v1+w1,, vn+wn〉

上面代码中,vw是两个多元矢量。它们的加运算,在 SIMD 下是一个指令、而不是 n 个指令完成的,这就大大提高了效率。这对于 3D 动画、图像处理、信号处理、数值处理、加密等运算是非常重要的。比如,Canvas 的getImageData()会将图像文件读成一个二进制数组,SIMD 就很适合对于这种数组的处理。

总的来说,SIMD 是数据并行处理(parallelism)的一种手段,可以加速一些运算密集型操作的速度。将来与 WebAssembly 结合以后,可以让 JavaScript 达到二进制代码的运行速度。

# 数据类型

SIMD 提供 12 种数据类型,总长度都是 128 个二进制位。

  • Float32x4:四个 32 位浮点数
  • Float64x2:两个 64 位浮点数
  • Int32x4:四个 32 位整数
  • Int16x8:八个 16 位整数
  • Int8x16:十六个 8 位整数
  • Uint32x4:四个无符号的 32 位整数
  • Uint16x8:八个无符号的 16 位整数
  • Uint8x16:十六个无符号的 8 位整数
  • Bool32x4:四个 32 位布尔值
  • Bool16x8:八个 16 位布尔值
  • Bool8x16:十六个 8 位布尔值
  • Bool64x2:两个 64 位布尔值

每种数据类型被x符号分隔成两部分,后面的部分表示通道数,前面的部分表示每个通道的宽度和类型。比如,Float32x4就表示这个值有 4 个通道,每个通道是一个 32 位浮点数。

每个通道之中,可以放置四种数据。

  • 浮点数(float,比如 1.0)
  • 带符号的整数(Int,比如-1)
  • 无符号的整数(Uint,比如 1)
  • 布尔值(Bool,包含truefalse两种值)

每种 SIMD 的数据类型都是一个函数方法,可以传入参数,生成对应的值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0);

上面代码中,变量a就是一个 128 位、包含四个 32 位浮点数(即四个通道)的值。

注意,这些数据类型方法都不是构造函数,前面不能加new,否则会报错。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var v = new SIMD.Float32x4(0, 1, 2, 3);
// TypeError: SIMD.Float32x4 is not a constructor

# 静态方法:数学运算

每种数据类型都有一系列运算符,支持基本的数学运算。

# SIMD.%type%.abs(),SIMD.%type%.neg()

abs方法接受一个 SIMD 值作为参数,将它的每个通道都转成绝对值,作为一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(-1, -2, 0, NaN);
SIMD.Float32x4.abs(a)
// Float32x4[1, 2, 0, NaN]

neg方法接受一个 SIMD 值作为参数,将它的每个通道都转成负值,作为一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(-1, -2, 3, 0);
SIMD.Float32x4.neg(a)
// Float32x4[1, 2, -3, -0]

var b = SIMD.Float64x2(NaN, Infinity);
SIMD.Float64x2.neg(b)
// Float64x2[NaN, -Infinity]

# SIMD.%type%.add(),SIMD.%type%.addSaturate()

add方法接受两个 SIMD 值作为参数,将它们的每个通道相加,作为一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0);
var b = SIMD.Float32x4(5.0, 10.0, 15.0, 20.0);
var c = SIMD.Float32x4.add(a, b);

上面代码中,经过加法运算,新的 SIMD 值为(6.0, 12.0, 18.0. 24.0)

addSaturate方法跟add方法的作用相同,都是两个通道相加,但是溢出的处理不一致。对于add方法,如果两个值相加发生溢出,溢出的二进制位会被丢弃; addSaturate方法则是返回该数据类型的最大值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Uint16x8(65533, 65534, 65535, 65535, 1, 1, 1, 1);
var b = SIMD.Uint16x8(1, 1, 1, 5000, 1, 1, 1, 1);
SIMD.Uint16x8.addSaturate(a, b);
// Uint16x8[65534, 65535, 65535, 65535, 2, 2, 2, 2]

var c = SIMD.Int16x8(32765, 32766, 32767, 32767, 1, 1, 1, 1);
var d = SIMD.Int16x8(1, 1, 1, 5000, 1, 1, 1, 1);
SIMD.Int16x8.addSaturate(c, d);
// Int16x8[32766, 32767, 32767, 32767, 2, 2, 2, 2]

上面代码中,Uint16的最大值是 65535,Int16的最大值是 32767。一旦发生溢出,就返回这两个值。

注意,Uint32x4Int32x4这两种数据类型没有addSaturate方法。

# SIMD.%type%.sub(),SIMD.%type%.subSaturate()

sub方法接受两个 SIMD 值作为参数,将它们的每个通道相减,作为一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(-1, -2, 3, 4);
var b = SIMD.Float32x4(3, 3, 3, 3);
SIMD.Float32x4.sub(a, b)
// Float32x4[-4, -5, 0, 1]

subSaturate方法跟sub方法的作用相同,都是两个通道相减,但是溢出的处理不一致。对于sub方法,如果两个值相减发生溢出,溢出的二进制位会被丢弃; subSaturate方法则是返回该数据类型的最小值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Uint16x8(5, 1, 1, 1, 1, 1, 1, 1);
var b = SIMD.Uint16x8(10, 1, 1, 1, 1, 1, 1, 1);
SIMD.Uint16x8.subSaturate(a, b)
// Uint16x8[0, 0, 0, 0, 0, 0, 0, 0]

var c = SIMD.Int16x8(-100, 0, 0, 0, 0, 0, 0, 0);
var d = SIMD.Int16x8(32767, 0, 0, 0, 0, 0, 0, 0);
SIMD.Int16x8.subSaturate(c, d)
// Int16x8[-32768, 0, 0, 0, 0, 0, 0, 0, 0]

上面代码中,Uint16的最小值是0Int16的最小值是-32678。一旦运算发生溢出,就返回最小值。

# SIMD.%type%.mul(),SIMD.%type%.div(),SIMD.%type%.sqrt()

mul方法接受两个 SIMD 值作为参数,将它们的每个通道相乘,作为一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(-1, -2, 3, 4);
var b = SIMD.Float32x4(3, 3, 3, 3);
SIMD.Float32x4.mul(a, b)
// Float32x4[-3, -6, 9, 12]

div方法接受两个 SIMD 值作为参数,将它们的每个通道相除,作为一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(2, 2, 2, 2);
var b = SIMD.Float32x4(4, 4, 4, 4);
SIMD.Float32x4.div(a, b)
// Float32x4[0.5, 0.5, 0.5, 0.5]

sqrt方法接受一个 SIMD 值作为参数,求出每个通道的平方根,作为一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var b = SIMD.Float64x2(4, 8);
SIMD.Float64x2.sqrt(b)
// Float64x2[2, 2.8284271247461903]

# SIMD.%FloatType%.reciprocalApproximation(),SIMD.%type%.reciprocalSqrtApproximation()

reciprocalApproximation方法接受一个 SIMD 值作为参数,求出每个通道的倒数(1 / x),作为一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1, 2, 3, 4);
SIMD.Float32x4.reciprocalApproximation(a);
// Float32x4[1, 0.5, 0.3333333432674408, 0.25]

reciprocalSqrtApproximation方法接受一个 SIMD 值作为参数,求出每个通道的平方根的倒数(1 / (x^0.5)),作为一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1, 2, 3, 4);
SIMD.Float32x4.reciprocalSqrtApproximation(a)
// Float32x4[1, 0.7071067690849304, 0.5773502588272095, 0.5]

注意,只有浮点数的数据类型才有这两个方法。

# SIMD.%IntegerType%.shiftLeftByScalar()

shiftLeftByScalar方法接受一个 SIMD 值作为参数,然后将每个通道的值左移指定的位数,作为一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Int32x4(1, 2, 4, 8);
SIMD.Int32x4.shiftLeftByScalar(a, 1);
// Int32x4[2, 4, 8, 16]

如果左移后,新的值超出了当前数据类型的位数,溢出的部分会被丢弃。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var ix4 = SIMD.Int32x4(1, 2, 3, 4);
var jx4 = SIMD.Int32x4.shiftLeftByScalar(ix4, 32);
// Int32x4[0, 0, 0, 0]

注意,只有整数的数据类型才有这个方法。

# SIMD.%IntegerType%.shiftRightByScalar()

shiftRightByScalar方法接受一个 SIMD 值作为参数,然后将每个通道的值右移指定的位数,返回一个新的 SIMD 值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Int32x4(1, 2, 4, -8);
SIMD.Int32x4.shiftRightByScalar(a, 1);
// Int32x4[0, 1, 2, -4]

如果原来通道的值是带符号的值,则符号位保持不变,不受右移影响。如果是不带符号位的值,则右移后头部会补0

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Uint32x4(1, 2, 4, -8);
SIMD.Uint32x4.shiftRightByScalar(a, 1);
// Uint32x4[0, 1, 2, 2147483644]

上面代码中,-8右移一位变成了2147483644,是因为对于 32 位无符号整数来说,-8的二进制形式是11111111111111111111111111111000,右移一位就变成了01111111111111111111111111111100,相当于2147483644

注意,只有整数的数据类型才有这个方法。

# 静态方法:通道处理

# SIMD.%type%.check()

check方法用于检查一个值是否为当前类型的 SIMD 值。如果是的,就返回这个值,否则就报错。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1, 2, 3, 9);

SIMD.Float32x4.check(a);
// Float32x4[1, 2, 3, 9]

SIMD.Float32x4.check([1,2,3,4]) // 报错
SIMD.Int32x4.check(a) // 报错
SIMD.Int32x4.check('hello world') // 报错

# SIMD.%type%.extractLane(),SIMD.%type%.replaceLane()

extractLane方法用于返回给定通道的值。它接受两个参数,分别是 SIMD 值和通道编号。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var t = SIMD.Float32x4(1, 2, 3, 4);
SIMD.Float32x4.extractLane(t, 2) // 3

replaceLane方法用于替换指定通道的值,并返回一个新的 SIMD 值。它接受三个参数,分别是原来的 SIMD 值、通道编号和新的通道值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var t = SIMD.Float32x4(1, 2, 3, 4);
SIMD.Float32x4.replaceLane(t, 2, 42)
// Float32x4[1, 2, 42, 4]

# SIMD.%type%.load()

load方法用于从二进制数组读入数据,生成一个新的 SIMD 值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = new Int32Array([1,2,3,4,5,6,7,8]);
SIMD.Int32x4.load(a, 0);
// Int32x4[1, 2, 3, 4]

var b = new Int32Array([1,2,3,4,5,6,7,8]);
SIMD.Int32x4.load(a, 2);
// Int32x4[3, 4, 5, 6]

load方法接受两个参数:一个二进制数组和开始读取的位置(从 0 开始)。如果位置不合法(比如-1或者超出二进制数组的大小),就会抛出一个错误。

这个方法还有三个变种load1()load2()load3(),表示从指定位置开始,只加载一个通道、二个通道、三个通道的值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 格式
SIMD.Int32x4.load(tarray, index)
SIMD.Int32x4.load1(tarray, index)
SIMD.Int32x4.load2(tarray, index)
SIMD.Int32x4.load3(tarray, index)

// 实例
var a = new Int32Array([1,2,3,4,5,6,7,8]);
SIMD.Int32x4.load1(a, 0);
// Int32x4[1, 0, 0, 0]
SIMD.Int32x4.load2(a, 0);
// Int32x4[1, 2, 0, 0]
SIMD.Int32x4.load3(a, 0);
// Int32x4[1, 2, 3,0]

# SIMD.%type%.store()

store方法用于将一个 SIMD 值,写入一个二进制数组。它接受三个参数,分别是二进制数组、开始写入的数组位置、SIMD 值。它返回写入值以后的二进制数组。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var t1 = new Int32Array(8);
var v1 = SIMD.Int32x4(1, 2, 3, 4);
SIMD.Int32x4.store(t1, 0, v1)
// Int32Array[1, 2, 3, 4, 0, 0, 0, 0]

var t2 = new Int32Array(8);
var v2 = SIMD.Int32x4(1, 2, 3, 4);
SIMD.Int32x4.store(t2, 2, v2)
// Int32Array[0, 0, 1, 2, 3, 4, 0, 0]

上面代码中,t1是一个二进制数组,v1是一个 SIMD 值,只有四个通道。所以写入t1以后,只有前四个位置有值,后四个位置都是 0。而t2是从 2 号位置开始写入,所以前两个位置和后两个位置都是 0。

这个方法还有三个变种store1()store2()store3(),表示只写入一个通道、二个通道和三个通道的值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var tarray = new Int32Array(8);
var value = SIMD.Int32x4(1, 2, 3, 4);
SIMD.Int32x4.store1(tarray, 0, value);
// Int32Array[1, 0, 0, 0, 0, 0, 0, 0]

# SIMD.%type%.splat()

splat方法返回一个新的 SIMD 值,该值的所有通道都会设成同一个预先给定的值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SIMD.Float32x4.splat(3);
// Float32x4[3, 3, 3, 3]
SIMD.Float64x2.splat(3);
// Float64x2[3, 3]

如果省略参数,所有整数型的 SIMD 值都会设定0,浮点型的 SIMD 值都会设成NaN

# SIMD.%type%.swizzle()

swizzle方法返回一个新的 SIMD 值,重新排列原有的 SIMD 值的通道顺序。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var t = SIMD.Float32x4(1, 2, 3, 4);
SIMD.Float32x4.swizzle(t, 1, 2, 0, 3);
// Float32x4[2,3,1,4]

上面代码中,swizzle方法的第一个参数是原有的 SIMD 值,后面的参数对应将要返回的 SIMD 值的四个通道。它的意思是新的 SIMD 的四个通道,依次是原来 SIMD 值的 1 号通道、2 号通道、0 号通道、3 号通道。由于 SIMD 值最多可以有 16 个通道,所以swizzle方法除了第一个参数以外,最多还可以接受 16 个参数。

下面是另一个例子。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0);
// Float32x4[1.0, 2.0, 3.0, 4.0]

var b = SIMD.Float32x4.swizzle(a, 0, 0, 1, 1);
// Float32x4[1.0, 1.0, 2.0, 2.0]

var c = SIMD.Float32x4.swizzle(a, 3, 3, 3, 3);
// Float32x4[4.0, 4.0, 4.0, 4.0]

var d = SIMD.Float32x4.swizzle(a, 3, 2, 1, 0);
// Float32x4[4.0, 3.0, 2.0, 1.0]

# SIMD.%type%.shuffle()

shuffle方法从两个 SIMD 值之中取出指定通道,返回一个新的 SIMD 值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1, 2, 3, 4);
var b = SIMD.Float32x4(5, 6, 7, 8);

SIMD.Float32x4.shuffle(a, b, 1, 5, 7, 2);
// Float32x4[2, 6, 8, 3]

上面代码中,ab一共有 8 个通道,依次编号为 0 到 7。shuffle根据编号,取出相应的通道,返回一个新的 SIMD 值。

# 静态方法:比较运算

# SIMD.%type%.equal(),SIMD.%type%.notEqual()

equal方法用来比较两个 SIMD 值ab的每一个通道,根据两者是否精确相等(a === b),得到一个布尔值。最后,所有通道的比较结果,组成一个新的 SIMD 值,作为掩码返回。notEqual方法则是比较两个通道是否不相等(a !== b)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1, 2, 3, 9);
var b = SIMD.Float32x4(1, 4, 7, 9);

SIMD.Float32x4.equal(a,b)
// Bool32x4[true, false, false, true]

SIMD.Float32x4.notEqual(a,b);
// Bool32x4[false, true, true, false]

# SIMD.%type%.greaterThan(),SIMD.%type%.greaterThanOrEqual()

greatThan方法用来比较两个 SIMD 值ab的每一个通道,如果在该通道中,a较大就得到true,否则得到false。最后,所有通道的比较结果,组成一个新的 SIMD 值,作为掩码返回。greaterThanOrEqual则是比较a是否大于等于b

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1, 6, 3, 11);
var b = SIMD.Float32x4(1, 4, 7, 9);

SIMD.Float32x4.greaterThan(a, b)
// Bool32x4[false, true, false, true]

SIMD.Float32x4.greaterThanOrEqual(a, b)
// Bool32x4[true, true, false, true]

# SIMD.%type%.lessThan(),SIMD.%type%.lessThanOrEqual()

lessThan方法用来比较两个 SIMD 值ab的每一个通道,如果在该通道中,a较小就得到true,否则得到false。最后,所有通道的比较结果,会组成一个新的 SIMD 值,作为掩码返回。lessThanOrEqual方法则是比较a是否等于b

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1, 2, 3, 11);
var b = SIMD.Float32x4(1, 4, 7, 9);

SIMD.Float32x4.lessThan(a, b)
// Bool32x4[false, true, true, false]

SIMD.Float32x4.lessThanOrEqual(a, b)
// Bool32x4[true, true, true, false]

# SIMD.%type%.select()

select方法通过掩码生成一个新的 SIMD 值。它接受三个参数,分别是掩码和两个 SIMD 值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(1, 2, 3, 4);
var b = SIMD.Float32x4(5, 6, 7, 8);

var mask = SIMD.Bool32x4(true, false, false, true);

SIMD.Float32x4.select(mask, a, b);
// Float32x4[1, 6, 7, 4]

上面代码中,select方法接受掩码和两个 SIMD 值作为参数。当某个通道对应的掩码为true时,会选择第一个 SIMD 值的对应通道,否则选择第二个 SIMD 值的对应通道。

这个方法通常与比较运算符结合使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(0, 12, 3, 4);
var b = SIMD.Float32x4(0, 6, 7, 50);

var mask = SIMD.Float32x4.lessThan(a,b);
// Bool32x4[false, false, true, true]

var result = SIMD.Float32x4.select(mask, a, b);
// Float32x4[0, 6, 3, 4]

上面代码中,先通过lessThan方法生成一个掩码,然后通过select方法生成一个由每个通道的较小值组成的新的 SIMD 值。

# SIMD.%BooleanType%.allTrue(),SIMD.%BooleanType%.anyTrue()

allTrue方法接受一个 SIMD 值作为参数,然后返回一个布尔值,表示该 SIMD 值的所有通道是否都为true

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Bool32x4(true, true, true, true);
var b = SIMD.Bool32x4(true, false, true, true);

SIMD.Bool32x4.allTrue(a); // true
SIMD.Bool32x4.allTrue(b); // false

anyTrue方法则是只要有一个通道为true,就返回true,否则返回false

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Bool32x4(false, false, false, false);
var b = SIMD.Bool32x4(false, false, true, false);

SIMD.Bool32x4.anyTrue(a); // false
SIMD.Bool32x4.anyTrue(b); // true

注意,只有四种布尔值数据类型(Bool32x4Bool16x8Bool8x16Bool64x2)才有这两个方法。

这两个方法通常与比较运算符结合使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var ax4    = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0);
var bx4    = SIMD.Float32x4(0.0, 6.0, 7.0, 8.0);
var ix4    = SIMD.Float32x4.lessThan(ax4, bx4);
var b1     = SIMD.Int32x4.allTrue(ix4); // false
var b2     = SIMD.Int32x4.anyTrue(ix4); // true

# SIMD.%type%.min(),SIMD.%type%.minNum()

min方法接受两个 SIMD 值作为参数,将两者的对应通道的较小值,组成一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(-1, -2, 3, 5.2);
var b = SIMD.Float32x4(0, -4, 6, 5.5);
SIMD.Float32x4.min(a, b);
// Float32x4[-1, -4, 3, 5.2]

如果有一个通道的值是NaN,则会优先返回NaN

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var c = SIMD.Float64x2(NaN, Infinity)
var d = SIMD.Float64x2(1337, 42);
SIMD.Float64x2.min(c, d);
// Float64x2[NaN, 42]

minNum方法与min的作用一模一样,唯一的区别是如果有一个通道的值是NaN,则会优先返回另一个通道的值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var ax4 = SIMD.Float32x4(1.0, 2.0, NaN, NaN);
var bx4 = SIMD.Float32x4(2.0, 1.0, 3.0, NaN);
var cx4 = SIMD.Float32x4.min(ax4, bx4);
// Float32x4[1.0, 1.0, NaN, NaN]
var dx4 = SIMD.Float32x4.minNum(ax4, bx4);
// Float32x4[1.0, 1.0, 3.0, NaN]

# SIMD.%type%.max(),SIMD.%type%.maxNum()

max方法接受两个 SIMD 值作为参数,将两者的对应通道的较大值,组成一个新的 SIMD 值返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(-1, -2, 3, 5.2);
var b = SIMD.Float32x4(0, -4, 6, 5.5);
SIMD.Float32x4.max(a, b);
// Float32x4[0, -2, 6, 5.5]

如果有一个通道的值是NaN,则会优先返回NaN

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var c = SIMD.Float64x2(NaN, Infinity)
var d = SIMD.Float64x2(1337, 42);
SIMD.Float64x2.max(c, d)
// Float64x2[NaN, Infinity]

maxNum方法与max的作用一模一样,唯一的区别是如果有一个通道的值是NaN,则会优先返回另一个通道的值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var c = SIMD.Float64x2(NaN, Infinity)
var d = SIMD.Float64x2(1337, 42);
SIMD.Float64x2.maxNum(c, d)
// Float64x2[1337, Infinity]

# 静态方法:位运算

# SIMD.%type%.and(),SIMD.%type%.or(),SIMD.%type%.xor(),SIMD.%type%.not()

and方法接受两个 SIMD 值作为参数,返回两者对应的通道进行二进制AND运算(&)后得到的新的 SIMD 值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Int32x4(1, 2, 4, 8);
var b = SIMD.Int32x4(5, 5, 5, 5);
SIMD.Int32x4.and(a, b)
// Int32x4[1, 0, 4, 0]

上面代码中,以通道0为例,1的二进制形式是00015的二进制形式是01001,所以进行AND运算以后,得到0001

or方法接受两个 SIMD 值作为参数,返回两者对应的通道进行二进制OR运算(|)后得到的新的 SIMD 值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Int32x4(1, 2, 4, 8);
var b = SIMD.Int32x4(5, 5, 5, 5);
SIMD.Int32x4.or(a, b)
// Int32x4[5, 7, 5, 13]

xor方法接受两个 SIMD 值作为参数,返回两者对应的通道进行二进制“异或”运算(^)后得到的新的 SIMD 值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Int32x4(1, 2, 4, 8);
var b = SIMD.Int32x4(5, 5, 5, 5);
SIMD.Int32x4.xor(a, b)
// Int32x4[4, 7, 1, 13]

not方法接受一个 SIMD 值作为参数,返回每个通道进行二进制“否”运算(~)后得到的新的 SIMD 值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Int32x4(1, 2, 4, 8);
SIMD.Int32x4.not(a)
// Int32x4[-2, -3, -5, -9]

上面代码中,1的否运算之所以得到-2,是因为在计算机内部,负数采用”2 的补码“这种形式进行表示。也就是说,整数n的负数形式-n,是对每一个二进制位取反以后,再加上 1。因此,直接取反就相当于负数形式再减去 1,比如1的负数形式是-1,再减去 1,就得到了-2

# 静态方法:数据类型转换

SIMD 提供以下方法,用来将一种数据类型转为另一种数据类型。

  • SIMD.%type%.fromFloat32x4()
  • SIMD.%type%.fromFloat32x4Bits()
  • SIMD.%type%.fromFloat64x2Bits()
  • SIMD.%type%.fromInt32x4()
  • SIMD.%type%.fromInt32x4Bits()
  • SIMD.%type%.fromInt16x8Bits()
  • SIMD.%type%.fromInt8x16Bits()
  • SIMD.%type%.fromUint32x4()
  • SIMD.%type%.fromUint32x4Bits()
  • SIMD.%type%.fromUint16x8Bits()
  • SIMD.%type%.fromUint8x16Bits()

带有Bits后缀的方法,会原封不动地将二进制位拷贝到新的数据类型;不带后缀的方法,则会进行数据类型转换。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var t = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0);
SIMD.Int32x4.fromFloat32x4(t);
// Int32x4[1, 2, 3, 4]

SIMD.Int32x4.fromFloat32x4Bits(t);
// Int32x4[1065353216, 1073741824, 1077936128, 1082130432]

上面代码中,fromFloat32x4是将浮点数转为整数,然后存入新的数据类型;fromFloat32x4Bits则是将二进制位原封不动地拷贝进入新的数据类型,然后进行解读。

Bits后缀的方法,还可以用于通道数目不对等的拷贝。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var t = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0);
SIMD.Int16x8.fromFloat32x4Bits(t);
// Int16x8[0, 16256, 0, 16384, 0, 16448, 0, 16512]

上面代码中,原始 SIMD 值t是 4 通道的,而目标值是 8 通道的。

如果数据转换时,原通道的数据大小,超过了目标通道的最大宽度,就会报错。

# 实例方法

# SIMD.%type%.prototype.toString()

toString方法返回一个 SIMD 值的字符串形式。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var a = SIMD.Float32x4(11, 22, 33, 44);
a.toString() // "SIMD.Float32x4(11, 22, 33, 44)"

# 实例:求平均值

正常模式下,计算n个值的平均值,需要运算n次。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function average(list) {
  var n = list.length;
  var sum = 0.0;
  for (var i = 0; i < n; i++) {
    sum += list[i];
  }
  return sum / n;
}

使用 SIMD,可以将计算次数减少到n次的四分之一。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function average(list) {
  var n = list.length;
  var sum = SIMD.Float32x4.splat(0.0);
  for (var i = 0; i < n; i += 4) {
    sum = SIMD.Float32x4.add(
      sum,
      SIMD.Float32x4.load(list, i)
    );
  }
  var total = SIMD.Float32x4.extractLane(sum, 0) +
              SIMD.Float32x4.extractLane(sum, 1) +
              SIMD.Float32x4.extractLane(sum, 2) +
              SIMD.Float32x4.extractLane(sum, 3);
  return total / n;
}

上面代码先是每隔四位,将所有的值读入一个 SIMD,然后立刻累加。然后,得到累加值四个通道的总和,再除以n就可以了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-02-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
js操作二进制数据
使用ArrayBuffer对象保存二进制数据,使用TypedArray和DataView 视图来读写数据。
风花一世月
2024/03/19
3340
js操作二进制数据
[go 标准库] strconv
go strconv 包提供了基本数据类型与 string 类型相互转换常用的处理函数。提供了如下操作接口:
柳公子
2021/05/06
9580
记一次SIMD指令优化计算的失败经历
书接上回 《统计一个数字二进制位1的个数》,现在我们已经知道如何快速计算出一个int64数字的二进制位1的个数,那么回到我们最初的需求,我们的目的是快速统计一个bitmap中二进制位1的个数,假设我们使用[]uint64来实现bitmap,那么如果要统计这个bitmap中二进制位1的个数,我们可以遍历每个元素,计算出每个uint64元素二进制位1的个数,最后加起来,代码大概如下:
Orlion
2024/09/02
1730
记一次SIMD指令优化计算的失败经历
ArrayBuffer
ArrayBuffer对象、TypedArray视图和DataView视图是 JavaScript 操作二进制数据的一个接口。这些对象早就存在,属于独立的规格(2011 年 2 月发布),ES6 将它们纳入了 ECMAScript 规格,并且增加了新的方法。它们都是以数组的语法处理二进制数据,所以统称为二进制数组。 这个接口的原始设计目的,与 WebGL 项目有关。所谓 WebGL,就是指浏览器与显卡之间的通信接口,为了满足 JavaScript 与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式。文本格式传递一个 32 位整数,两端的 JavaScript 脚本与显卡都要进行格式转化,将非常耗时。这时要是存在一种机制,可以像 C 语言那样,直接操作字节,将 4 个字节的 32 位整数,以二进制形式原封不动地送入显卡,脚本的性能就会大幅提升。
小小杰啊
2022/12/21
2.6K0
浮点,多少老司机的血泪史
浮点值应该是我们比较熟悉的一种数据类型,工作中经常用到,会进行比较、计算、转换等等,这些数值操作往往隐藏着很多陷阱,有的可能对计算值产生微小偏差而被忽略,有的可能造成重大软件事故。
用户6879030
2024/06/05
2010
浮点,多少老司机的血泪史
web 直播流的解析
本文作者:ivweb villainthr Web 进制操作是一个比较底层的话题,因为平常做业务的时候根本用不到太多,或者说,根本用不到。 老铁,没毛病 那什么情况会用到呢? canvas webso
腾讯IVWEB团队
2017/07/14
4K2
web 直播流的解析
【初识Go】| Day2 数据类型、关键字、标识符
数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
yussuy
2020/12/16
5850
【初识Go】| Day2 数据类型、关键字、标识符
听GPT 讲Rust源代码--library/portable-simd
spectral_norm.rs是一个示例程序,它展示了如何使用Portable SIMD库中的SIMD(Single Instruction Multiple Data)功能来实现频谱规范化算法。该示例程序是Rust源代码中的一个文件,位于rust/library/portable-simd/crates/core_simd/examples目录下。
fliter
2024/02/26
2150
分门别类输入输出,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang基本数据类型和输入输出EP03
    前文再续,Go lang和Python一样,基础数据类型有着很多分类,分门别类,一应俱全。它们对应着不同的使用场景,分别是:整形、浮点、字符、字符串、布尔等等。常用的基本数据类型经常会参与日常业务逻辑的运算、判断以及输入输出操作。
用户9127725
2022/08/08
3490
分门别类输入输出,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang基本数据类型和输入输出EP03
03 . Go之数据类型和运算符
指针(pointer), 数组(array) , 切片(slice) , 映射(map) , 函数(function), 结构体(struct) , 通道(channel)
iginkgo18
2022/05/09
5740
Golang之旅27-Golang知识点总结1
字符串是由一连串的字符连接起来的字符序列,使用的utf-8编码标识的unicode文本。
皮大大
2021/03/02
3990
Pandas 2.2 中文官方教程和指南(九·一)
在这里,我们讨论了与 pandas 数据结构共同的许多基本功能。首先,让我们创建一些示例对象,就像我们在 10 分钟入门 pandas 部分中所做的那样:
ApacheCN_飞龙
2024/05/24
4440
PHP数据类型
可以用十进制、二进制、八进制、十六进制表示,前面加上“+”和“-”表示正整数和负整数
白胡杨同学
2020/04/02
2.4K0
PHP数据类型
go-基本数据类型和运算符
Go语言中的基本数据类型有: 整型、浮点型、布尔型、字符串、数组、切片、map、函数、结构体和通道(channel)等。
新人小试
2020/02/29
6410
python中一些数据处理库
Numpy是Python的一个很重要的第三方库,很多其他科学计算的第三方库都是以Numpy为基础建立的。Numpy的一个重要特性是它的数组计算。
用户7886150
2021/01/05
9200
Webassembly初识
首先,它是一种解释性语言,大神最开始的设计目标用户就是“非专业编程人员和设计师”,避免了非专业人士对编译器了解的需要,解释性语言就是边解释边执行,与编译性语言的先编译后执行相比,执行速度慢了很多;
CIKEY
2019/03/18
1.1K0
Julia(数学运算和基本函数)
Julia提供了所有其数字原始类型的基本算术运算符和按位运算符的完整集合,并提供了标准数学函数的全面集合的可移植且有效的实现。
云深无际
2021/04/14
1.9K0
Julia(数学运算和基本函数)
JS中的二进制数据处理
  在现有的计算机中,二进制常常以字节数组的形式存在于程序当中。例如在C#里面,就用byte[],标准C里面没有byte类型,但可以通过typedef把byte定义为unsigned char的别名,效果是一样的。JS设计之初似乎就没想过要处理二进制,对于字节的概念可以说是非常非常的模糊。如果要表达字节数组,那么似乎只能用一个普通数组来表示。
有赞coder
2021/05/13
3.8K0
JS中的二进制数据处理
使用ES6新特性开发微信小程序(4)
Symbol Type ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。 Symbol值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。 let s =
极乐君
2018/02/05
1.8K0
Go语言基础之基本数据类型
整型分为以下两个大类: 按长度分为:int8、int16、int32、int64 对应的无符号整型:uint8、uint16、uint32、uint64 其中,uint8就是我们熟知的byte型,int16对应C语言中的short型,int64对应C语言中的long型。
twelvecoder
2021/12/24
5570
相关推荐
js操作二进制数据
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • # SIMD
    • # 概述
    • # 数据类型
    • # 静态方法:数学运算
      • # SIMD.%type%.abs(),SIMD.%type%.neg()
      • # SIMD.%type%.add(),SIMD.%type%.addSaturate()
      • # SIMD.%type%.sub(),SIMD.%type%.subSaturate()
      • # SIMD.%type%.mul(),SIMD.%type%.div(),SIMD.%type%.sqrt()
      • # SIMD.%FloatType%.reciprocalApproximation(),SIMD.%type%.reciprocalSqrtApproximation()
      • # SIMD.%IntegerType%.shiftLeftByScalar()
      • # SIMD.%IntegerType%.shiftRightByScalar()
    • # 静态方法:通道处理
      • # SIMD.%type%.check()
      • # SIMD.%type%.extractLane(),SIMD.%type%.replaceLane()
      • # SIMD.%type%.load()
      • # SIMD.%type%.store()
      • # SIMD.%type%.splat()
      • # SIMD.%type%.swizzle()
      • # SIMD.%type%.shuffle()
    • # 静态方法:比较运算
      • # SIMD.%type%.equal(),SIMD.%type%.notEqual()
      • # SIMD.%type%.greaterThan(),SIMD.%type%.greaterThanOrEqual()
      • # SIMD.%type%.lessThan(),SIMD.%type%.lessThanOrEqual()
      • # SIMD.%type%.select()
      • # SIMD.%BooleanType%.allTrue(),SIMD.%BooleanType%.anyTrue()
      • # SIMD.%type%.min(),SIMD.%type%.minNum()
      • # SIMD.%type%.max(),SIMD.%type%.maxNum()
    • # 静态方法:位运算
      • # SIMD.%type%.and(),SIMD.%type%.or(),SIMD.%type%.xor(),SIMD.%type%.not()
    • # 静态方法:数据类型转换
    • # 实例方法
      • # SIMD.%type%.prototype.toString()
    • # 实例:求平均值
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档