1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-24 18:32:22 +00:00

docs(simd): add simd chapter

This commit is contained in:
ruanyf 2016-10-04 18:41:24 +08:00
parent 96b6c5a3fd
commit bf30299b48
5 changed files with 408 additions and 84 deletions

View File

@ -1252,3 +1252,62 @@ async function chainAnimationsAsync(elem, animations) {
可以看到Async函数的实现最简洁最符合语义几乎没有语义不相关的代码。它将Generator写法中的自动执行器改在语言层面提供不暴露给用户因此代码量最少。如果使用Generator写法自动执行器需要用户自己提供。 可以看到Async函数的实现最简洁最符合语义几乎没有语义不相关的代码。它将Generator写法中的自动执行器改在语言层面提供不暴露给用户因此代码量最少。如果使用Generator写法自动执行器需要用户自己提供。
## 异步遍历器
《遍历器》一章说过Iterator接口是一种数据遍历的协议只要调用遍历器的`next`方法,就会得到一个对象值`{value, done}`。其中,`value`表示当前的数据的值,`done`是一个布尔值,表示遍历是否结束。
这意味着,`next`方法是同步的,只要调用就必须立刻返回值。也就是说,一旦执行`next`方法,就必须同步地得到`value``done`这两方面的信息。这对于同步操作当然没有问题但对于异步操作就不太合适了。目前的解决方法是Generator函数里面的异步操作返回一个Thunk函数或者Promise对象`value`属性是一个Thunk函数或者Promise对象等待以后返回真正的值`done`属性则还是同步产生的。
目前,有一个[提案](https://github.com/tc39/proposal-async-iteration),为异步操作提供原生的遍历器接口,即`value``done`这两个属性都是异步产生这称为”异步遍历器“Async Iterator
### 异步遍历的接口
异步遍历器的最大的语法特点,就是调用遍历器的`next`方法返回的是一个Promise对象。
```javascript
asyncIterator
.next()
.then(
({ value, done }) => /* ... */
);
```
上面代码中,`asyncIterator`是一个异步遍历器,调用`next`方法以后返回一个Promise对象。因此可以使用`then`方法指定这个Promise对象的状态变为`resolve`以后的回调函数。回调函数的参数,则是一个具有`value``done`两个属性的对象,这个跟同步遍历器是一样的。
我们知道,一个对象的同步遍历器的接口,部署在`Symbol.iterator`属性上面。同样地,对象的异步遍历器接口,部署在`Symbol.asyncIterator`属性上面。不管是什么样的对象,只要它的`Symbol.asyncIterator`属性有值,就表示应该对它进行异步遍历。
### for await...of
前面介绍过,`for...of`循环用于遍历同步的Iterator接口。新引入的`for await...of`循环则是用于遍历异步的Iterator接口。
```javascript
for await (const line of readLines(filePath)) {
console.log(line);
}
```
上面代码中,`readLines`函数返回一个异步遍历器,每次调用它的`next`方法就会返回一个Promise对象。`await`表示等待这个Promise对象`resolve`,一旦完成,变量`line`就是Promise对象返回的`value`值。
### 异步Generator函数
就像Generator函数返回一个同步遍历器对象一样异步Generator函数的作用是返回一个异步遍历器对象。
在语法上异步Generator函数就是`async`函数与Generator函数的结合。
```javascript
async function* readLines(path) {
let file = await fileOpen(path);
try {
while (!file.EOF) {
yield await file.readLine();
}
} finally {
await file.close();
}
}
```
上面代码中,异步操作前面使用`await`关键字标明,`next`方法所在的中断之处使用`yield`关键字标明。
注意,普通的`async`函数返回的是一个Promise对象而异步Generator函数返回的是一个异步Iterator对象。

View File

@ -1446,9 +1446,9 @@ sum(1, 100000)
## 函数参数的尾逗号 ## 函数参数的尾逗号
ES7有一个[提案](https://github.com/jeffmo/es-trailing-function-commas),允许函数的最后一个参数有尾逗号trailing comma ECMAScript 2017将[允许](https://github.com/jeffmo/es-trailing-function-commas)函数的最后一个参数有尾逗号trailing comma
目前,函数定义和调用时,都不允许有参数的尾逗号。 此前,函数定义和调用时,都不允许最后一个参数后面出现逗号。
```javascript ```javascript
function clownsEverywhere( function clownsEverywhere(
@ -1462,7 +1462,9 @@ clownsEverywhere(
); );
``` ```
如果以后要在函数的定义之中添加参数,就势必还要添加一个逗号。这对版本管理系统来说,就会显示,添加逗号的那一行也发生了变动。这看上去有点冗余,因此新提案允许定义和调用时,尾部直接有一个逗号。 上面代码中,如果在`param2``bar`后面加一个逗号,就会报错。
这样的话,如果以后修改代码,想为函数`clownsEverywhere`添加第三个参数,就势必要在第二个参数后面添加一个逗号。这对版本管理系统来说,就会显示,添加逗号的那一行也发生了变动。这看上去有点冗余,因此新的语法允许定义和调用时,尾部直接有一个逗号。
```javascript ```javascript
function clownsEverywhere( function clownsEverywhere(

View File

@ -7,6 +7,7 @@
- [ECMAScript® 2017 Language Specification](https://tc39.github.io/ecma262/)ECMAScript 2017规格草案 - [ECMAScript® 2017 Language Specification](https://tc39.github.io/ecma262/)ECMAScript 2017规格草案
- [ECMAScript Current Proposals](https://github.com/tc39/ecma262): ECMAScript当前的所有提案 - [ECMAScript Current Proposals](https://github.com/tc39/ecma262): ECMAScript当前的所有提案
- [ECMAScript Active Proposals](https://github.com/tc39/proposals): 已经进入正式流程的提案 - [ECMAScript Active Proposals](https://github.com/tc39/proposals): 已经进入正式流程的提案
- [ECMAscript proposals](https://github.com/hemanth/es-next)从阶段0到阶段4的所有提案列表
- [ECMAScript Daily](https://ecmascript-daily.github.io/): TC39委员会的动态 - [ECMAScript Daily](https://ecmascript-daily.github.io/): TC39委员会的动态
## 综合介绍 ## 综合介绍

View File

@ -1,4 +1,4 @@
# SIMD 的用法 # SIMD
## 概述 ## 概述
@ -47,7 +47,7 @@ v + w = 〈v1, …, vn〉+ 〈w1, …, wn〉
## 数据类型 ## 数据类型
SIMD提供多种数据类型 SIMD提供12种数据类型总长度都是128个二进制位
- Float32x4四个32位浮点数 - Float32x4四个32位浮点数
- Float64x2两个64位浮点数 - Float64x2两个64位浮点数
@ -62,9 +62,16 @@ SIMD提供多种数据类型。
- Bool8x16十六个8位布尔值 - Bool8x16十六个8位布尔值
- Bool64x2两个64位布尔值 - Bool64x2两个64位布尔值
每种数据类型被`x`号分隔成两部分,后面的部分表示通道数,前面的部分表示每个通道的宽度和类型。比如,`Float32x4`就表示这个值有4个通道每个通道是一个32位浮点数。 每种数据类型被`x`号分隔成两部分,后面的部分表示通道数,前面的部分表示每个通道的宽度和类型。比如,`Float32x4`就表示这个值有4个通道每个通道是一个32位浮点数。
每种数据类型都是一个方法,可以传入参数,生成对应的值。 每个通道之中,可以放置四种数据。
- 浮点数float比如1.0
- 带符号的整数Int比如-1
- 无符号的整数Uint比如1
- 布尔值Bool包含`true``false`两种值)
每种SIMD的数据类型都是一个函数方法可以传入参数生成对应的值。
```javascript ```javascript
var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0);
@ -79,46 +86,35 @@ var v = new SIMD.Float32x4(0, 1, 2, 3);
// TypeError: SIMD.Float32x4 is not a constructor // TypeError: SIMD.Float32x4 is not a constructor
``` ```
如果所有数据通道都是同样的值,可以使用`splat`方法生成值。 ## 静态方法:数学运算
每种数据类型都有一系列运算符,支持基本的数学运算。
### SIMD.%type%.abs()SIMD.%type%.neg()
`abs`方法接受一个SIMD值作为参数将它的每个通道都转成绝对值作为一个新的SIMD值返回。
```javascript ```javascript
var v = SIMD.Float32x4.splat(0.0); var a = SIMD.Float32x4(-1, -2, 0, NaN);
SIMD.Float32x4.abs(a)
// Float32x4[1, 2, 0, NaN]
``` ```
上面代码中,`v`的四个通道都是`0.0` `neg`方法接受一个SIMD值作为参数将它的每个通道都转成负值作为一个新的SIMD值返回。
如果要取出单个通道的值,可以使用`extractLane`方法。
```javascript ```javascript
var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); var a = SIMD.Float32x4(-1, -2, 3, 0);
var b = SIMD.Float32x4.extractLane(a, 0); // 1.0 SIMD.Float32x4.neg(a)
// Float32x4[1, 2, -3, -0]
var b = SIMD.Float64x2(NaN, Infinity);
SIMD.Float64x2.neg(b)
// Float64x2[NaN, -Infinity]
``` ```
上面代码中,`extractLane`方法的第一个参数是一个SIMD值第二个参数是通道的编号从0开始 ### SIMD.%type%.add()SIMD.%type%.addSaturate()
如果要修改某个通道的值,可以使用`replaceLane`方法。 `add`方法接受两个SIMD值作为参数将它们的每个通道相加作为一个新的SIMD值返回。
```javascript
var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0);
var c = SIMD.Float32x4.replaceLane(a, 0, 5.0);
```
上面代码中经过替换后得到一个新的SIMD值`(5.0, 2.0, 3.0, 4.0)`。可以看到,`replaceLane`接受三个参数SIMD值、通道的编号从0开始、新的值。
## 方法:数学运算
每种数据类型都有一系列运算符,下面是其中的一些。
- float32x4.abs(v):返回`v`的绝对值
- float32x4.neg(v):返回`v`的绝对值的负值
- float32x4.sqrt(v):返回`v`的平方根
- float32x4.add(v, w)`v``w`对应项的相加
- float32x4.mul(v, w)`v``w`对应项的相乘
- float32x4.equal(v, w):比较`v``w`对应项是否相等,返回的布尔值组成一个`uint32x4`的值
### SIMD.%type%.add()
`add`方法接受两个SIMD值作为参数将它们的每个通道相加返回一个新的SIMD值。
```javascript ```javascript
var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0);
@ -128,20 +124,102 @@ var c = SIMD.Float32x4.add(a, b);
上面代码中经过加法运算新的SIMD值为`(6.0, 12.0, 18.0. 24.0)` 上面代码中经过加法运算新的SIMD值为`(6.0, 12.0, 18.0. 24.0)`
### SIMD.%type%.mul() `addSaturate`方法跟`add`方法的作用相同,都是两个通道相加,但是溢出的处理不一致。对于`add`方法,如果两个值相加发生溢出,溢出的二进制位会被丢弃; `addSaturate`方法则是返回该数据类型的最大值。
`mul`方法接受两个SIMD值作为参数将它们的每个通道相乘返回一个新的SIMD值。 ```javascript
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。一旦发生溢出就返回这两个值。
注意,`Uint32x4``Int32x4`这两种数据类型没有`addSaturate`方法。
### SIMD.%type%.sub()SIMD.%type%.subSaturate()
`sub`方法接受两个SIMD值作为参数将它们的每个通道相减作为一个新的SIMD值返回。
```javascript ```javascript
var a = SIMD.Float32x4(-1, -2, 3, 4); var a = SIMD.Float32x4(-1, -2, 3, 4);
var b = SIMD.Float32x4(3, 3, 3, 3); var b = SIMD.Float32x4(3, 3, 3, 3);
SIMD.Float32x4.mul(a, b); SIMD.Float32x4.sub(a, b)
// Float32x4[-4, -5, 0, 1]
```
`subSaturate`方法跟`sub`方法的作用相同,都是两个通道相减,但是溢出的处理不一致。对于`sub`方法,如果两个值相减发生溢出,溢出的二进制位会被丢弃; `subSaturate`方法则是返回该数据类型的最小值。
```javascript
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`的最小值是`0``subSaturate`的最小值是`-32678`。一旦运算发生溢出,就返回最小值。
### SIMD.%type%.mul()SIMD.%type%.div()SIMD.%type%.sqrt()
`mul`方法接受两个SIMD值作为参数将它们的每个通道相乘作为一个新的SIMD值返回。
```javascript
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] // Float32x4[-3, -6, 9, 12]
``` ```
### SIMD.%type%.shiftLeftByScalar() `div`方法接受两个SIMD值作为参数将它们的每个通道相除作为一个新的SIMD值返回。
`shiftLeftByScalar`方法接受一个SIMD值作为参数然后将每个通道的值左移指定的位数返回一个新的SIMD值。 ```javascript
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
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
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
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 ```javascript
var a = SIMD.Int32x4(1, 2, 4, 8); var a = SIMD.Int32x4(1, 2, 4, 8);
@ -157,7 +235,9 @@ var jx4 = SIMD.Int32x4.shiftLeftByScalar(ix4, 32);
// Int32x4[0, 0, 0, 0] // Int32x4[0, 0, 0, 0]
``` ```
### SIMD.%type%.shiftRightByScalar() 注意,只有整数的数据类型才有这个方法。
### SIMD.%IntegerType%.shiftRightByScalar()
`shiftRightByScalar`方法接受一个SIMD值作为参数然后将每个通道的值右移指定的位数返回一个新的SIMD值。 `shiftRightByScalar`方法接受一个SIMD值作为参数然后将每个通道的值右移指定的位数返回一个新的SIMD值。
@ -177,7 +257,41 @@ SIMD.Uint32x4.shiftRightByScalar(a, 1);
上面代码中,`-8`右移一位变成了`2147483644`是因为对于32位无符号整数来说`-8`的二进制形式是`11111111111111111111111111111000`,右移一位就变成了`01111111111111111111111111111100`,相当于`2147483644` 上面代码中,`-8`右移一位变成了`2147483644`是因为对于32位无符号整数来说`-8`的二进制形式是`11111111111111111111111111111000`,右移一位就变成了`01111111111111111111111111111100`,相当于`2147483644`
## 方法:通道处理 注意,只有整数的数据类型才有这个方法。
## 静态方法:通道处理
### SIMD.%type%.check()
`check`方法用于检查一个值是否为当前类型的SIMD值。如果是的就返回这个值否则就报错。
```javascript
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
var t = SIMD.Float32x4(1, 2, 3, 4);
SIMD.Float32x4.extractLane(t, 2) // 3
```
`replaceLane`方法用于替换指定通道的值并返回一个新的SIMD值。它接受三个参数分别是原来的SIMD值、通道编号和新的通道值。
```javascript
var t = SIMD.Float32x4(1, 2, 3, 4);
SIMD.Float32x4.replaceLane(t, 2, 42)
// Float32x4[1, 2, 42, 4]
```
### SIMD.%type%.load() ### SIMD.%type%.load()
@ -214,6 +328,33 @@ SIMD.Int32x4.load3(a, 0);
// Int32x4[1, 2, 3,0] // Int32x4[1, 2, 3,0]
``` ```
### SIMD.%type%.store()
`store`方法用于将一个SIMD值写入一个二进制数组。它接受三个参数分别是二进制数组、开始写入的数组位置、SIMD值。它返回写入值以后的二进制数组。
```javascript
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
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() ### SIMD.%type%.splat()
`splat`方法返回一个新的SIMD值该值的所有通道都会设成同一个预先给定的值。 `splat`方法返回一个新的SIMD值该值的所有通道都会设成同一个预先给定的值。
@ -269,30 +410,51 @@ SIMD.Float32x4.shuffle(a, b, 1, 5, 7, 2);
上面代码中,`a``b`一共有8个通道依次编号为0到7。`shuffle`根据编号取出相应的通道返回一个新的SIMD值。 上面代码中,`a``b`一共有8个通道依次编号为0到7。`shuffle`根据编号取出相应的通道返回一个新的SIMD值。
## 方法:比较运算 ## 静态方法:比较运算
### SIMD.%type%.greaterThan() ### SIMD.%type%.equal()SIMD.%type%.notEqual()
`greatThan`方法用来比较两个SIMD值`a``b`的每一个通道,如果在该通道中,`a`较大就得到`true`,否则得到`false`。最后所有通道的比较结果会组成一个新的SIMD值作为掩码返回。 `equal`方法用来比较两个SIMD值`a``b`的每一个通道,根据两者是否精确相等(`a === b`得到一个布尔值。最后所有通道的比较结果组成一个新的SIMD值作为掩码返回。`notEqual`方法则是比较两个通道是否不相等(`a !== b`)。
```javascript
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值`a``b`的每一个通道,如果在该通道中,`a`较大就得到`true`,否则得到`false`。最后所有通道的比较结果组成一个新的SIMD值作为掩码返回。`greaterThanOrEqual`则是比较`a`是否大于等于`b`
```javascript ```javascript
var a = SIMD.Float32x4(1, 6, 3, 11); var a = SIMD.Float32x4(1, 6, 3, 11);
var b = SIMD.Float32x4(1, 4, 7, 9); var b = SIMD.Float32x4(1, 4, 7, 9);
var mask = SIMD.Float32x4.greaterThan(a,b); SIMD.Float32x4.greaterThan(a, b)
// Bool32x4[false, true, false, true] // Bool32x4[false, true, false, true]
SIMD.Float32x4.greaterThanOrEqual(a, b)
// Bool32x4[true, true, false, true]
``` ```
### SIMD.%type%.lessThan() ### SIMD.%type%.lessThan()SIMD.%type%.lessThanOrEqual()
`lessThan`方法用来比较两个SIMD值`a``b`的每一个通道,如果在该通道中,`a`较小就得到`true`,否则得到`false`。最后所有通道的比较结果会组成一个新的SIMD值作为掩码返回。 `lessThan`方法用来比较两个SIMD值`a``b`的每一个通道,如果在该通道中,`a`较小就得到`true`,否则得到`false`。最后所有通道的比较结果会组成一个新的SIMD值作为掩码返回。`lessThanOrEqual`方法则是比较`a`是否等于`b`
```javascript ```javascript
var a = SIMD.Float32x4(1, 2, 3, 11); var a = SIMD.Float32x4(1, 2, 3, 11);
var b = SIMD.Float32x4(1, 4, 7, 9); var b = SIMD.Float32x4(1, 4, 7, 9);
var mask = SIMD.Float32x4.lessThan(a,b); SIMD.Float32x4.lessThan(a, b)
// Bool32x4[false, true, true, false] // Bool32x4[false, true, true, false]
SIMD.Float32x4.lessThanOrEqual(a, b)
// Bool32x4[true, true, true, false]
``` ```
### SIMD.%type%.select() ### SIMD.%type%.select()
@ -326,7 +488,7 @@ var result = SIMD.Float32x4.select(mask, a, b);
上面代码中,先通过`lessThan`方法生成一个掩码,然后通过`select`方法生成一个由每个通道的较小值组成的新的SIMD值。 上面代码中,先通过`lessThan`方法生成一个掩码,然后通过`select`方法生成一个由每个通道的较小值组成的新的SIMD值。
### SIMD.%type%.allTrue()SIMD.%type%.anyTrue() ### SIMD.%BooleanType%.allTrue()SIMD.%BooleanType%.anyTrue()
`allTrue`方法接受一个SIMD值作为参数然后返回一个布尔值表示该SIMD值的所有通道是否都为`true` `allTrue`方法接受一个SIMD值作为参数然后返回一个布尔值表示该SIMD值的所有通道是否都为`true`
@ -348,6 +510,8 @@ SIMD.Bool32x4.anyTrue(a); // false
SIMD.Bool32x4.anyTrue(b); // true SIMD.Bool32x4.anyTrue(b); // true
``` ```
注意,只有四种布尔值数据类型(`Bool32x4``Bool16x8``Bool8x16``Bool64x2`)才有这两个方法。
这两个方法通常与比较运算符结合使用。 这两个方法通常与比较运算符结合使用。
```javascript ```javascript
@ -360,7 +524,7 @@ var b2 = SIMD.Int32x4.anyTrue(ix4); // true
### SIMD.%type%.min()SIMD.%type%.minNum() ### SIMD.%type%.min()SIMD.%type%.minNum()
`min`方法接受两个SIMD值作为参数它们的每个通道的较小值组成一个新的SIMD值返回。 `min`方法接受两个SIMD值作为参数两者的对应通道的较小值组成一个新的SIMD值返回。
```javascript ```javascript
var a = SIMD.Float32x4(-1, -2, 3, 5.2); var a = SIMD.Float32x4(-1, -2, 3, 5.2);
@ -369,7 +533,7 @@ SIMD.Float32x4.min(a, b);
// Float32x4[-1, -4, 3, 5.2] // Float32x4[-1, -4, 3, 5.2]
``` ```
如果有一个通道的值是`NaN`,则会返回`NaN` 如果有一个通道的值是`NaN`,则会优先返回`NaN`
```javascript ```javascript
var c = SIMD.Float64x2(NaN, Infinity) var c = SIMD.Float64x2(NaN, Infinity)
@ -378,7 +542,7 @@ SIMD.Float64x2.min(c, d);
// Float64x2[NaN, 42] // Float64x2[NaN, 42]
``` ```
`minNum`方法与`min`方法的作用一模一样,唯一的区别是如果有一个通道的值是`NaN`,则会优先返回另一个通道的值。 `minNum`方法与`min`的作用一模一样,唯一的区别是如果有一个通道的值是`NaN`,则会优先返回另一个通道的值。
```javascript ```javascript
var ax4 = SIMD.Float32x4(1.0, 2.0, NaN, NaN); var ax4 = SIMD.Float32x4(1.0, 2.0, NaN, NaN);
@ -389,6 +553,130 @@ var dx4 = SIMD.Float32x4.minNum(ax4, bx4);
// Float32x4[1.0, 1.0, 3.0, NaN] // Float32x4[1.0, 1.0, 3.0, NaN]
``` ```
### SIMD.%type%.max()SIMD.%type%.maxNum()
`max`方法接受两个SIMD值作为参数将两者的对应通道的较大值组成一个新的SIMD值返回。
```javascript
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
var c = SIMD.Float64x2(NaN, Infinity)
var d = SIMD.Float64x2(1337, 42);
SIMD.Float64x2.max(c, d)
// Float64x2[NaN, Infinity]
```
`maxNum`方法与`max`的作用一模一样,唯一的区别是如果有一个通道的值是`NaN`,则会优先返回另一个通道的值。
```javascript
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
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`的二进制形式是`0001``5`的二进制形式是`01001`,所以进行`AND`运算以后,得到`0001`
`or`方法接受两个SIMD值作为参数返回两者对应的通道进行二进制`OR`运算(`|`后得到的新的SIMD值。
```javascript
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
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
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
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
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
var a = SIMD.Float32x4(11, 22, 33, 44);
a.toString() // "SIMD.Float32x4(11, 22, 33, 44)"
```
## 实例:求平均值 ## 实例:求平均值
正常模式下,计算`n`个值的平均值,需要运算`n`次。 正常模式下,计算`n`个值的平均值,需要运算`n`次。
@ -426,33 +714,6 @@ function average(list) {
上面代码先是每隔四位将所有的值读入一个SIMD然后立刻累加。然后得到累加值四个通道的总和再除以`n`就可以了。 上面代码先是每隔四位将所有的值读入一个SIMD然后立刻累加。然后得到累加值四个通道的总和再除以`n`就可以了。
## 二进制数组
SIMD可以与二进制数组结合生成数组实例。
```javascript
var _f64x2 = new Float64Array(_f32x4.buffer);
var _i32x4 = new Int32Array(_f32x4.buffer);
var _i16x8 = new Int16Array(_f32x4.buffer);
var _i8x16 = new Int8Array(_f32x4.buffer);
var _ui32x4 = new Uint32Array(_f32x4.buffer);
var _ui16x8 = new Uint16Array(_f32x4.buffer);
var _ui8x16 = new Uint8Array(_f32x4.buffer);
```
下面是一个例子。
```javascript
// a 和 b 是float32x4数组实例
function addArrays(a, b) {
var c = new Float32x4Array(a.length);
for (var i = 0; i < a.length; i++) {
c[i] = SIMD.float32x4.add(a[i], b[i]);
}
return c;
}
```
## 参考链接 ## 参考链接
- TC39, [SIMD.js Stage 2](https://docs.google.com/presentation/d/1MY9NHrHmL7ma7C8dyNXvmYNNGgVmmxXk8ZIiQtPlfH4/edit#slide=id.p19) - TC39, [SIMD.js Stage 2](https://docs.google.com/presentation/d/1MY9NHrHmL7ma7C8dyNXvmYNNGgVmmxXk8ZIiQtPlfH4/edit#slide=id.p19)

View File

@ -17,7 +17,6 @@
1. [对象的扩展](#docs/object) 1. [对象的扩展](#docs/object)
1. [Symbol](#docs/symbol) 1. [Symbol](#docs/symbol)
1. [Proxy和Reflect](#docs/proxy) 1. [Proxy和Reflect](#docs/proxy)
1. [二进制数组](#docs/arraybuffer)
1. [Set和Map数据结构](#docs/set-map) 1. [Set和Map数据结构](#docs/set-map)
1. [Iterator和for...of循环](#docs/iterator) 1. [Iterator和for...of循环](#docs/iterator)
1. [Generator函数](#docs/generator) 1. [Generator函数](#docs/generator)
@ -28,6 +27,8 @@
1. [Module](#docs/module) 1. [Module](#docs/module)
1. [编程风格](#docs/style) 1. [编程风格](#docs/style)
1. [读懂规格](#docs/spec) 1. [读懂规格](#docs/spec)
1. [二进制数组](#docs/arraybuffer)
1. [SIMD](#docs/simd)
1. [参考链接](#docs/reference) 1. [参考链接](#docs/reference)
## 其他 ## 其他