AVX/AVX2による整数型から浮動小数点型への型変換

# はじめに
浮動小数点型であるfloat/double型から整数型に変換します．
変換には，cvt命令とcvtt命令があります．
cvt命令は，MXCSRレジスタに応じて丸めを行い，cvtt命令は，常に切り捨てで丸めを行います．

#### _mm256_cvtepi32_ps (AVX)
```cpp
__m256 _mm256_cvtepi32_ps (__m256i a)
asm: vcvtdq2ps ymm, ymm/m256
```

**動作**  
cvtepi32_ps
![v1_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67908/5d0e8b7d-609e-9235-09f3-016bc75b9d2f.png)
**CPI/Uops**  

| Architecture | Latency | Throughput |Uops|
|--------------|:-------:|:----------:|:--:|
| Alderlake    |    4    |    0.5     |  - |
| Icelake      |    4    |    0.5     |  1 |
| Skylake      |    4    |    0.5     |  1 |
| Broadwell    |    3    |     1      |  1 |
| Haswell      |    3    |     1      |  1 |
| Ivy Bridge   |    3    |     1      |  1 |
| Sandy Bridge |    3    |     1      |  1 |
| Zen3         |    3    |    0.5     |  1 |
| Zen2         |    3    |     1      |  1 |
| Zen          |    4    |     2      |  2 |

* メモリから読み込んでもスループットは変わりません．

**説明**  

8つの要素のintをfloatに変換します．

型を変換するだけですが，演算コストがかかります．
IntelのCPUの場合，Alderlakeを除いて浮動小数点の加算と同じコストがかかります．
AMDの場合，Zen2までは加算よりもすこしだけ重たい処理になります．
intからfloat変換はメモリからロードしてもスループットは変わりません．

#### _mm256_cvt|cvttps_epi32 (AVX)
```cpp
__m256i _mm256_cvtps_epi32 (__m256 a)
asm: vcvtps2dq ymm, ymm/m256
__m256i _mm256_cvttps_epi32 (__m256 a)
asm: vcvttps2dq ymm, ymm/m256
```

**動作**  
cvtps_epi32/cvttps_epi32
![v1_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67908/5d0e8b7d-609e-9235-09f3-016bc75b9d2f.png)
![cvt_32_64.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67908/8b6dce96-6dfc-c6b8-5326-2946b6aa79a8.png)


**CPI/Uops**  

| Architecture | Latency | Throughput |Uops|
|--------------|:-------:|:----------:|:--:|
| Alderlake    |    4    |    0.5     |  - |
| Icelake      |    4    |    0.5     |  1 |
| Skylake      |    4    |    0.5     |  1 |
| Broadwell    |    3    |     1      |  1 |
| Haswell      |    3    |     1      |  1 |
| Ivy Bridge   |    3    |     1      |  1 |
| Sandy Bridge |    3    |     1      |  1 |
| Zen3         |    3    |    0.5     |  1 |
| Zen2         |    3    |     1      |  1 |
| Zen          |    4    |     2      |  2 |

* 逆変換とパフォーマンスは同じです．
* メモリから読み込んでもスループットは変わりません．

**説明**  

8つの要素のfloatにint変換します．

cvt命令の場合，intへの変換の場合，MXCSRコントロールレジスタの状態に合わせて浮動小数点を丸めます．
cvt**t**命令の場合，MXCSRコントロールレジスタの状態を**無視**して，常時0への切り捨てのキャストします．
つまり下記のようなキャストを行います．

```cpp
double a=10.5;
int b = (int)a;
```

打ち切る場合も，打ち切らない場合もパフォーマンスは同じで，この逆変換とも同じです．
IntelのCPUの場合，Alderlakeを除いて浮動小数点の加算と同じコストがかかります．
AMDの場合，Zen2までは加算よりもすこしだけ重たい処理になります．

#### _mm256_cvtepi32_pd (AVX)
```cpp
__m256d _mm256_cvtepi32_pd (__m128i a)
asm: vcvtdq2pd ymm, xmm/m128
```
**動作**  

cvtepi32_pd
![cvt_32_64.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67908/9b454b6e-d6d3-3af2-9175-16eb09f0db7f.png)



**CPI/Uops**  

| Architecture | Latency | Throughput |Uops|
|--------------|:-------:|:----------:|:--:|
| Alderlake    |    7    |      1     |  - |
| Icelake      |    7    |      1     |  2 |
| Skylake      |    7    |      1     |  2 |
| Broadwell    |    6    |      1     |  2 |
| Haswell      |    6    |      1     |  2 |
| Ivy Bridge   |    5    |      1     |  2 |
| Sandy Bridge |    5    |      1     |  2 |
| Zen3         | 4(≤11)  |    1(0.5)  |2(1)|
| Zen2         | 4(≤11)  |     1(1)   |2(1)|
| Zen          | 7(≤14)  |     2(3)   |4(7)|

* floatへの変換よりも重たいです．
* かっこつきはメモリから読み込んだ場合です．
* AIDA64: Zen L:7, T:2, Zen2 L:4, T:1, Zen3 L:4, T:0.5
* Anger: Zen L:6, T:2, Zen2 L:5, T:1, Zen3 L:6, T:1
* uopsによると，メモリから読み込むとZen3のスループットは倍になります．
* IntelのCPUは，doubleからintへの変換も同コストです．

**説明**  

4つの要素のintをdoubleに変換します．
doubleとintのサイズが異なり，出力は2倍のサイズとなるため，引数の型は`__m128i`です．
epi64からの変換ではないことに注意してください．

#### _mm256_cvtpd|cvttpd_epi32 (AVX)
```cpp
__m128i _mm256_cvtpd_epi32 (__m256d a)
asm: vcvtpd2dq ymm, ymm/m256
__m128i _mm256_cvttpd_epi32 (__m256d a)
asm: vcvttpd2dq ymm, ymm/m256
```

**動作**  

cvtpd_epi32/cvttpd_epi32
![cvt_64_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67908/4e7057e2-9c7c-6a1b-e988-d1ff33c35854.png)


**CPI/Uops**  

| Architecture | Latency | Throughput |Uops|
|--------------|:-------:|:----------:|:--:|
| Alderlake    |    7    |      1     |  - |
| Icelake      |    7    |      1     |  2 |
| Skylake      |    7    |      1     |  2 |
| Broadwell    |    6    |      1     |  2 |
| Haswell      |    6    |      1     |  2 |
| Ivy Bridge   |    5    |      1     |  2 |
| Sandy Bridge |    5    |      1     |  2 |
| Zen3         |    6    |      1     |  2 |
| Zen2         |    6    |      1     |  2 |
| Zen          |    7    |      2     |  4 |

* AIDA64 Zen L:7, T:1, Zen2 L:6.1, T:0.68 Zen3 L:6.5, T:0.43
* メモリから読み込んでも動作は変わりません．

**説明**  
下位4つの要素のdoubleにint変換します．

cvt命令の場合，intへの変換の場合，MXCSRコントロールレジスタの状態に合わせて浮動小数点を丸めます．
cvt**t**命令の場合，MXCSRコントロールレジスタの状態を**無視**して，常時0切り捨てのキャストします．
つまり下記のようなキャストを行います．

```cpp
double a=10.5;
int b = (int)a;
```

doubleとintのサイズが異なり，出力は半分のサイズとなるため，戻り値の型は`__m128i`です．
epi64への変換ではないことに注意してください．

Zenアーキテクチャもスループットに違いはありません．