Top Banner
半半半半半半半半半 half @tomoaki_teshima 半半 CV 半半半
41

Cvim saisentan 半精度浮動小数点数 half

Jan 08, 2017

Download

Engineering

tomoaki0705
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Cvim saisentan 半精度浮動小数点数 half

半精度浮動小数点数half

@tomoaki_teshima関東 CV 勉強会

Page 2: Cvim saisentan 半精度浮動小数点数 half

Float の演算に必要なメモリ量• 必要なメモリ(ディスク)量はどれぐらい?

Page 3: Cvim saisentan 半精度浮動小数点数 half

サイズをもっと減らしたい!• RGB 3 bytes / pixel• float 4 bytes / pixel• どうやって減らしますか?

Page 4: Cvim saisentan 半精度浮動小数点数 half

本日の発表• Deep Learning をはじめとする機械学習• 一切出てきません

• Python• 一切出てきません• C/C++ とアセンブラが半々

• Chainer などの便利ライブラリ• 一切出てきません

Page 5: Cvim saisentan 半精度浮動小数点数 half

本日の発表• 半精度浮動小数点数 half の説明• ARM の対応• ARM(SIMD) の対応• Intel, AMD(x86) の対応• CUDA の対応

Page 6: Cvim saisentan 半精度浮動小数点数 half

浮動小数点数のフォーマットIEEE754

64bit = double 倍精度

32bit = float 単精度

16bit = half 半精度

符号 bit

指数部仮数部

1

1

1

11bit 52bit

23bit

10bit5bit

8bit

Page 7: Cvim saisentan 半精度浮動小数点数 half

ARM には fp16 がある

https://ja.wikipedia.org/wiki/半精度浮動小数点数

Page 8: Cvim saisentan 半精度浮動小数点数 half

用意するもの• Linux が走る ARM• Raspberry Pi zero/1/2/3• ODROID XU4/C2• Jetson TK1/TX1• PINE64• 赤字は 64bit 対応

• 実機買った方が開発には最適!

Page 9: Cvim saisentan 半精度浮動小数点数 half

試して見ようint main(int argc, char**argv)

{

printf("Hello World !!\n");

__fp16 halfPrecision = 1.5f;

printf("half precision:%f\n“, halfPrecision);

printf("half precision:sizeof %d\n“, sizeof(halfPrecision));

printf("half precision:0x%04x\n", *(short*)(void*)&halfPrecision);

float original[] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f,

9.0f,10.0f,11.0f,12.0f,13.0f,14.0f,15.0f,16.0f,};

for (unsigned int i = 0;i < 16;i++)

{

__fp16 stub = original[i];

printf(“%2d 0x%04x\n", (int)original[i], *(short*)&stub);

}

return 0;

}

https://github.com/tomoaki0705/sampleFp16

Page 10: Cvim saisentan 半精度浮動小数点数 half

ビルドしてみよう

• オプションを付ける必要あり• ARM 上の gcc でないと、 unknown option

エラー

$ gcc -std=c99 -mfp16-format=ieee main.c

Page 11: Cvim saisentan 半精度浮動小数点数 half

実行結果 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0

1/2

1/1024

1/41/81/16

1/321/64

1/1281/256

1/512

2(17− 15)×(1+ 12+ 14 )=22× 74=7

符号ビット (+)

指数部 (17)

仮数部

指数部が全部 0 だと subnormal 、指数部が全部 1 だと Inf もしくは NaN と定義されている

Page 12: Cvim saisentan 半精度浮動小数点数 half

おしまい• 浮動小数点の符号 / 復号化は意外に面倒• 2byte で浮動小数点を表した• めでたしめでたし

Page 13: Cvim saisentan 半精度浮動小数点数 half

アセンブラで見てみよう•まさかのソフト実装!

•がっかり・・・・•一日一回感謝のアセンブラ

$ gcc –S -std=c99 -mfp16-format=ieee –O main.c.s main.c

movw r3, #15872 ←0x3e00strh r3, [r7, #8] @ __fp16 ←stack に保存ldrh r3, [r7, #8] @ __fp16 ←stack から読込mov r0, r3 @ __fp16 ← レジスタに移動bl __gnu_h2f_ieee ← 関数コール (half2float)

Page 14: Cvim saisentan 半精度浮動小数点数 half

ARM の half 変換命令•half ←→float の変換命令• VCVTB.F16.F32  ( float→half )• VCVTB.F32.F16  ( half→float )• VCVTT.F16.F32  ( float→half )• VCVTT.F32.F16  ( half→float )

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204ij/CJAGIFIJ.html

Page 15: Cvim saisentan 半精度浮動小数点数 half

Half 変換命令は拡張機能•実行時のプロセッサに FPU 命令が無い場合が普通にある• FPU もオプションで追加する$ gcc –mfp16-format=ieee main.c    ↓$ gcc –mfp16-format=ieee –mfpu=vfpv4 main.c

Page 16: Cvim saisentan 半精度浮動小数点数 half

アセンブラで見てみよう 2

movw r3, #15872strh r3, [r7, #8] @ __fp16add r2, r7, #8vld1.16 {d7[2]}, [r2]vcvtb.f32.f16 s15, s15

movw r3, #15872strh r3, [r7, #8] @ __fp16ldrh r3, [r7, #8] @ __fp16mov r0, r3 @ __fp16bl __gnu_h2f_ieee

FPU オプション無し FPU=vfpv4

Page 17: Cvim saisentan 半精度浮動小数点数 half

ARM で half のままの演算• half ←→float の変換のみ• VCVTB.F16.F32• VCVTB.F32.F16• VCVTT.F16.F32• VCVTT.F32.F16

• 直接 half で演算すると floatに自動的に cast される

Page 18: Cvim saisentan 半精度浮動小数点数 half

まとめ• ARM• FPU を指定すると HW の変換命令を使用する• あるのは変換命令だけ、演算時に float に cast される• めでたしめでたし

• ARM(SIMD)• Intel, AMD (x86)• CUDA

Page 19: Cvim saisentan 半精度浮動小数点数 half

ARM での fp16 命令 (SIMD)

• vcvt はベクトルの V

• SIMD で演算してみよう!• 浮動小数点間の変換• float16x4_t vcvt_f16_f32(float32x4_t a);• VCVT.F16.F32 d0, q0

• float32x4_t vcvt_f32_f16(float16x4_t a);• VCVT.F32.F16 q0, d0

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0348bj/BABGABJH.html

Page 20: Cvim saisentan 半精度浮動小数点数 half

ベクトル演算してみようconst unsigned int cParallel = 8;for (unsigned int x = 0;x <= cSize - cParallel;x += cParallel){ uint8x8_t srcInteger = vld1_u8(src+x); // load 64bits float16x4_t gainHalfLow = *(float16x4_t*)(gain + x ); // load 32bits float16x4_t gainHalfHigh = *(float16x4_t*)(gain + x + 4 ); // load 32bits uint16x8_t srcIntegerShort = vmovl_u8(srcInteger); // uchar -> ushort uint32x4_t srcIntegerLow = vmovl_u16(vget_low_s16 (srcIntegerShort)); // ushort -> uint uint32x4_t srcIntegerHigh = vmovl_u16(vget_high_s16(srcIntegerShort)); // ushort -> uint float32x4_t srcFloatLow = vcvtq_f32_u32(srcIntegerLow ); // uint -> float float32x4_t srcFloatHigh = vcvtq_f32_u32(srcIntegerHigh); // uint -> float float32x4_t gainFloatLow = vcvt_f32_f16(gainHalfLow ); // half -> float float32x4_t gainFloatHigh = vcvt_f32_f16(gainHalfHigh); // half -> float float32x4_t dstFloatLow = vmulq_f32(srcFloatLow, gainFloatLow ); // float * float float32x4_t dstFloatHigh = vmulq_f32(srcFloatHigh, gainFloatHigh); // float * float uint32x4_t dstIntegerLow = vcvtq_u32_f32(dstFloatLow ); // float -> uint uint32x4_t dstIntegerHigh = vcvtq_u32_f32(dstFloatHigh); // float -> uint uint16x8_t dstIntegerShort = vcombine_u16(vmovn_u16(dstIntegerLow), vmovn_u16(dstIntegerHigh)); // uint -> ushort uint8x8_t dstInteger = vmovn_u16(dstIntegerShort); // ushort -> uchar vst1_u8(dst+x, dstInteger); // store}

https://github.com/tomoaki0705/sampleFp16Vector

Page 21: Cvim saisentan 半精度浮動小数点数 half

さすがに読みにくいのでconst unsigned int cParallel = 8;for (unsigned int x = 0;x <= cSize - cParallel;x += cParallel){ uchar8 srcInteger = load_uchar8(src+x); // load 64bits half4 gainHalfLow = load_half4(gain + x ); // load 32bits half4 gainHalfHigh = load_half4(gain + x + 4 ); // load 32bits ushort8 srcIntegerShort = convert_uchar8_ushort8(srcInteger); // uchar -> ushort uint4 srcIntegerLow = convert_ushort8_lo_uint4(srcIntegerShort); // ushort -> uint uint4 srcIntegerHigh = convert_ushort8_hi_uint4(srcIntegerShort); // ushort -> uint float4 srcFloatLow = convert_uint4_float4(srcIntegerLow ); // uint -> float float4 srcFloatHigh = convert_uint4_float4(srcIntegerHigh); // uint -> float float4 gainFloatLow = convert_half4_float4(gainHalfLow ); // half -> float float4 gainFloatHigh = convert_half4_float4(gainHalfHigh); // half -> float float4 dstFloatLow = multiply_float4(srcFloatLow , gainFloatLow ); // float * float float4 dstFloatHigh = multiply_float4(srcFloatHigh, gainFloatHigh); // float * float uint4 dstIntegerLow = convert_float4_uint4(dstFloatLow ); // float -> uint uint4 dstIntegerHigh = convert_float4_uint4(dstFloatHigh); // float -> uint ushort8 dstIntegerShort = convert_uint4_ushort8(dstIntegerLow, dstIntegerHigh); // uint -> ushort uchar8 dstInteger = convert_ushort8_uchar8(dstIntegerShort); // ushort -> uchar store_uchar8(dst + x, dstInteger); // store}

Page 22: Cvim saisentan 半精度浮動小数点数 half

ビルドしてみよう• -mfpu オプションで half の指定だけではダメ• half かつ NEON(SIMD) に対応してる FPU を指定する• 詳細は参考リンク

vfpvfpv3vfpv3-fp16vfpv3-d16vfpv3-d16-fp16vfpv3xdvfpv3xd-fp16neonneon-fp16vfpv4vfpv4-d16fpv4-sp-d16neon-vfpv4fp-armv8crypto-neon-fp-armv8

参考 :mfpu のオプション一覧

http://dench.flatlib.jp/opengl/fpu_vfphttp://tessy.org/wiki/index.php?ARM%A4%CEFPU

Page 23: Cvim saisentan 半精度浮動小数点数 half

アセンブラで見てみよう

ちゃんと変換命令が使用されている

Page 24: Cvim saisentan 半精度浮動小数点数 half

まとめ• ARM• めでたしめでたし

• ARM(SIMD)• オプションで fp16 命令かつ neon 命令に対応した

FPU を指定する• めでたしめでたし

• Intel,AMD (x86)• CUDA

Page 25: Cvim saisentan 半精度浮動小数点数 half

x86 の half 対応• F16C 拡張命令セット

https://en.wikipedia.org/wiki/F16C

Page 26: Cvim saisentan 半精度浮動小数点数 half

ベクトル演算してみようconst unsigned int cParallel = 8;for (unsigned int x = 0;x <= cSize - cParallel;x += cParallel){ __m128i srcInteger = _mm_loadl_epi64((__m128i const *)(src + x)); // load 64bits __m128i gainHalfLow = _mm_loadl_epi64((__m128i const *)(gain + x )); // load 32bits __m128i gainHalfHigh = _mm_loadl_epi64((__m128i const *)(gain + x + 4)); // load 32bits __m128i srcIntegerShort = _mm_unpacklo_epi8(srcInteger, v_zero); // uchar -> ushort __m128i srcIntegerLow = _mm_unpacklo_epi16(srcIntegerShort, v_zero); // ushort -> uint __m128i srcIntegerHigh = _mm_unpackhi_epi16(srcIntegerShort, v_zero); // ushort -> uint __m128i srcFloatLow = _mm_cvtepi32_ps(srcIntegerLow ); // uint -> float __m128i srcFloatHigh = _mm_cvtepi32_ps(srcIntegerHigh); // uint -> float __m128 gainFloatLow = _mm_cvtph_ps(gainHalfLow ); // half -> float __m128 gainFloatHigh = _mm_cvtph_ps(gainHalfHigh); // half -> float __m128 dstFloatLow = _mm_mul_ps(srcFloatLow , gainFloatLow ); // float * float __m128 dstFloatHigh = _mm_mul_ps(srcFloatHigh, gainFloatHigh); // float * float __m128i dstIntegerLow = _mm_cvtps_epi32(dstFloatLow ); // float -> uint __m128i dstIntegerHigh = _mm_cvtps_epi32(dstFloatHigh); // float -> uint __m128i dstIntegerShort = _mm_packs_epi32(dstIntegerLow, dstIntegerHigh); // uint -> ushort __m128i dstInteger = _mm_packus_epi16(dstIntegerShort, v_zero); // ushort -> uchar _mm_storel_epi64((__m128i *)(dst + x), dstInteger); // store}

https://github.com/tomoaki0705/sampleFp16Vector

Page 27: Cvim saisentan 半精度浮動小数点数 half

さすがに読みにくいのでconst unsigned int cParallel = 8;for (unsigned int x = 0;x <= cSize - cParallel;x += cParallel){ uchar8 srcInteger = load_uchar8(src+x); // load 64bits half4 gainHalfLow = load_half4(gain + x ); // load 32bits half4 gainHalfHigh = load_half4(gain + x + 4 ); // load 32bits ushort8 srcIntegerShort = convert_uchar8_ushort8(srcInteger); // uchar -> ushort uint4 srcIntegerLow = convert_ushort8_lo_uint4(srcIntegerShort); // ushort -> uint uint4 srcIntegerHigh = convert_ushort8_hi_uint4(srcIntegerShort); // ushort -> uint float4 srcFloatLow = convert_uint4_float4(srcIntegerLow ); // uint -> float float4 srcFloatHigh = convert_uint4_float4(srcIntegerHigh); // uint -> float float4 gainFloatLow = convert_half4_float4(gainHalfLow ); // half -> float float4 gainFloatHigh = convert_half4_float4(gainHalfHigh); // half -> float float4 dstFloatLow = multiply_float4(srcFloatLow , gainFloatLow ); // float * float float4 dstFloatHigh = multiply_float4(srcFloatHigh, gainFloatHigh); // float * float uint4 dstIntegerLow = convert_float4_uint4(dstFloatLow ); // float -> uint uint4 dstIntegerHigh = convert_float4_uint4(dstFloatHigh); // float -> uint ushort8 dstIntegerShort = convert_uint4_ushort8(dstIntegerLow, dstIntegerHigh);// uint -> ushort uchar8 dstInteger = convert_ushort8_uchar8(dstIntegerShort); // ushort -> uchar store_uchar8(dst + x, dstInteger); // store}

$ gcc -mf16c main.cpp

Page 28: Cvim saisentan 半精度浮動小数点数 half

アセンブラで見てみよう• Debug モードでビルド• Inline 関数が展開されていない

Page 29: Cvim saisentan 半精度浮動小数点数 half

アセンブラで見てみよう• Release モードでビルド• Cmake なら

RelWithDebInfo モードちゃんと変換命令が使用されている(vcvtph2ps)

Page 30: Cvim saisentan 半精度浮動小数点数 half

アセンブラで見てみよう (gcc)• VS 同様 Debug モード• inline 関数は展開されていない

Page 31: Cvim saisentan 半精度浮動小数点数 half

アセンブラで見てみよう (gcc)• Release モードでビルド• 逆アセンブリ

ちゃんと変換命令が使用されている(vcvtph2ps)

Page 32: Cvim saisentan 半精度浮動小数点数 half

まとめ• ARM• めでたしめでたし

• ARM(SIMD)• めでたしめでたし

• Intel,AMD (x86)• x86 でも SSE の中に変換命令がある• Ivy Bridge 以降の Intel と Piledriver 以降の AMD で利用可 • めでたしめでたし

• CUDAhttps://blogs.msdn.microsoft.com/chuckw/2012/09/11/directxmath-f16c-and-fma/

Page 33: Cvim saisentan 半精度浮動小数点数 half

CUDAunsigned short a = g_indata[y*imgw+x];float gain;gain = __half2float(a);

float b = imageData[(y*imgw+x)*3 ];float g = imageData[(y*imgw+x)*3+1];float r = imageData[(y*imgw+x)*3+2];

g_odata[(y*imgw+x)*3 ] = clamp(b * gain, 0.0f, 255.0f);g_odata[(y*imgw+x)*3+1] = clamp(g * gain, 0.0f, 255.0f);g_odata[(y*imgw+x)*3+2] = clamp(r * gain, 0.0f, 255.0f);

Page 34: Cvim saisentan 半精度浮動小数点数 half

half を使う最大のメリット• GPU へ転送するバイト数が半分で済む

メモリ (GPU 上 )

Page 35: Cvim saisentan 半精度浮動小数点数 half

まとめ• ARM

• めでたしめでたし• ARM(SIMD)

• めでたしめでたし• Intel,AMD (x86)

• めでたしめでたし• CUDA

• CUDA 7.5 から正式サポート• Half での演算命令は Pascal から搭載予定→搭載発表← New!!• 一部の演算命令は既に Jetson TX1 で利用可能• 変換命令自体は割と古い GPU でも HW 的に存在するhttp://www.slideshare.net/NVIDIAJapan/1071-gpu-cuda-75maxwellhttp://pc.watch.impress.co.jp/docs/news/event/20160406_751833.html

Page 36: Cvim saisentan 半精度浮動小数点数 half

各プラットフォームの half 対応プラットフォーム 単一の変数 SIMDでの変換 Fp16のままの演算ARM ◯ ◯ ×X86 × ◯ ×

CUDA(Maxwell 以前 ) ◯ ◯ ×

CUDA(Pascal 以降 ) ◯ ◯ ←New!◯←New!

Page 37: Cvim saisentan 半精度浮動小数点数 half

半精度浮動小数点数の限界 – オーバーフロー• float の最大• 指数部 8bit 、仮数部 23bit→ 10E38 まで扱える• signed int の最大 + 2,147,483,647 より大きい

• half の最大• 指数部 5bit 、仮数部 10bit→65504 まで扱える• unsigned short の最大 +65536 より小さい!

Page 38: Cvim saisentan 半精度浮動小数点数 half

半精度浮動小数点数の限界 – 丸め誤差• float の丸め誤差

• 16777216(=2^24) までは整数を正確に表記できる• half の丸め誤差

• 2048 (=2^11) までは整数を正確に表記できる• 1024-2048 のレンジだと小数点以下の情報は失われる• 512-1024 のレンジだと 0.5 刻みでしか表現できない

• Ex. 180.5 + 178.2 + 185.2 + 150.3 + 160.3 = 854.5• 正しい平均値 170.9• Half で計算 171.0 ← 丸め誤差 0.1

Page 39: Cvim saisentan 半精度浮動小数点数 half

そもそものきっかけ• CodeIQ で出題された「マヨイドーロ問題」に挑戦• フィボナッチ数を求める計算

• 再帰関数で計算• メモ化再帰で計算• 指数関数で計算

𝑝𝑛=1√5 {( 1+√5

2 )𝑛

−( 1−√52 )

𝑛}pow 関数 で計算

マヨイドーロ問題 - https://codeiq.jp/q/2549 解説 - https://codeiq.jp/magazine/2015/12/35521/

Page 40: Cvim saisentan 半精度浮動小数点数 half

指数で計算する場合n Fn pnの結果 桁数 (10

進 )桁数 (2進 )

73 806515533049393 806515533049393 15 5074 1304969544928657 1304969544928657 16 5175 2111485077978050 2111485077978050 16 5176 3416454622906707 3416454622906706 16 5277 5527939700884757 5527939700884756 16 5378 8944394323791464 8944394323791464 16 5379 14472334024676221 14472334024676218 17 54

Page 41: Cvim saisentan 半精度浮動小数点数 half

参考文献1. 半精度浮動小数点数 - Wikipediahttps://ja.wikipedia.org/wiki/半精度浮動小数点数

2. tomoaki0705/sampleFp16: sample code to treat FP16 on ARMhttps://github.com/tomoaki0705/sampleFp16

3. ARM Information Centerhttp://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204ij/CJAGIFIJ.html

4. ARM Information Centerhttp://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0348bj/BABGABJH.html

5. tomoaki0705/sampleFp16Vector: float16bit sample code on x86 and ARMhttps://github.com/tomoaki0705/sampleFp16Vector

6. opengl:fpu_vfp [HYPER でんち ]http://dench.flatlib.jp/opengl/fpu_vfp

7. ARM の FPU - AkiWiki http://tessy.org/wiki/index.php?ARM%A4%CEFPU

8. F16C - Wikipedia, the free encyclopedia https://en.wikipedia.org/wiki/F16C

9. DirectXMath: F16C and FMA | Games for Windows and the DirectX SDK https://blogs.msdn.microsoft.com/chuckw/2012/09/11/directxmath-f16c-and-fma/

10. 1071: GPU コンピューティング最新情報 ~ CUDA 7.5 と Maxwell アーキテクチャ ~http://www.slideshare.net/NVIDIAJapan/1071-gpu-cuda-75maxwell

11. 【イベントレポート】次世代 GPU アーキテクチャ「 Pascal 」が明らかに ~ HBM2 による 720GB/sec の超広帯域など - PC Watch http://pc.watch.impress.co.jp/docs/news/event/20160406_751833.html

12. 結城浩の「マヨイドーロ問題」 | CodeIQ https://codeiq.jp/q/2549

13. 結城浩の「マヨイドーロ問題」解説| CodeIQ MAGAZINEhttps://codeiq.jp/magazine/2015/12/35521/