Tegra K1はかなりパワフルだと思う.Ubuntuもすこしゆっくりだが普通に動くし,ブラウジングも問題なくできる.極端に重い処理は,CUDAでGPUに投げることができる.電力当たりのスループットに関して言えば,汎用PCに対してアドバンテージがある.
難点を挙げるのならCPUがARMコアであることだ.ほとんどのCUDAアプリケーションが,x86 CPUがホストであることを前提に書かれている.CUDAのカーネル自体は簡単に移植できるだろうが,そのアプリで使用しているライブラリはどうだろうか?
また,ホストCPUの性能,メモリ量,メモリバンド幅,などを勘案すると,やはり計算資源として扱うには足りない.そういう意味では「GTXやTesla向けに書かれたCUDAアプリをTegraで動かせる」のではなく,「計算資源に乏しいモバイル環境にもCUDAが動く環境が来た」というのが正しい表現だと思う.
本題だが,最近Tegra K1でかなり重い処理をリアルタイムでや(ろうとする)る機会があった.
元のコードは,かなりGTX980とかで数分~数時間かけて実行するのが前提のアプリケーションで,ミリ秒処理はあまり考えられていない.
そのまま実行すると,Tegra K1でリアルタイム処理するには遅すぎるので,Atomの置換機能を使ってディレクトリ全体で250箇所くらいをfloatからdoubleへ変更した.そして,そのままでは動かないので少し修正.
nvprofで性能解析したところ,double→floatでおよそ4倍の処理速度になった.
2倍になると思っていたのだが4倍だった.あまり調べていないが,倍精度浮動小数点型の操作には4倍近いクロックがかかるのかもしれない.そういえば,Kepler世代は倍精度が遅いので有名だった.
汎用コンピューターだと,「浮動小数点型で迷ったらdoubleを使っとけ」というのがかなり一般的なのだが,GPGPUの世界だとまだまだfloat全盛期だなあと思った話.
とにかくTegraを触れたのはいい機会だった.今は,半精度だが「1TFLOPS」という大台に乗せてきたTegra X1が気になる.
2015年7月31日金曜日
2015年7月22日水曜日
ベンチマークが速すぎた話
ステンシル計算のベンチマークを最適化していたら,下のように主要な計算部分のループの処理時間が異常に速くなった.
元のコード:約70ms
私のコード:0.0004ms
どう考えてもおかしい.x10とかx100とかならまだ分かるが,元がベンチマークでそれなりに早く動くように書かれているはずなのにx10万はやりすぎだ.こんなに早くステンシルができたらJournalに通ってしまう.
そもそも,
コードを見ても,元からほとんどいじっていないので分からない.
不思議なことに,最適化オプションを-O3から-O0にすると,元のコードと同じくらいの「まともな」実行時間になった.
どうやら最適化でゴッソリ何かを削っているようだ.
コンパイルオプションに-save-tempsを付けてアセンブリを読んでみる.
まず,行数が元のコードが1200行ほどなのに,私のものは160行程度であった.
元のコードが同じなのにこれは如何に?
計算部分を見てみる.こういう時にprintfなどのライブラリコールを呼んでおくと良い目印になると思った.
私のコード
call omp_get_wtime
leaq .LC1(%rip), %rcx
movapd %xmm0, %xmm6
movl $548720, %edx
call printf
call omp_get_wtime
元のコード
call omp_get_wtime
movsd .LC4(%rip), %xmm12
movl $10, 340(%rsp)
movapd %xmm0, %xmm10
movq %r13, %r11
movapd %xmm12, %xmm13
.L26:
movq $12800, 248(%rsp)
++400行省略++
leaq .LC5(%rip), %rcx
movq %r11, %r13
movl $548720, %edx
call printf
call omp_get_wtime
コンパイラは賢い.ループ回数を記録するつもりで1ループごとに++していた変数に対しても,いきなりループ回数の合計を定数として突っ込んでprintしている.(下線部)
とりあえず,私のコードでは一番時間のかかるはずの計算部分の約400行がごっそり抜けている.
元のコードではこの計算の後に検算をする関数を読んでいたのだが,ループで計算した結果はその時だけしか使われない.私のコードでは,デバッグのためにその関数のcallを省いていたのが原因だったようだ.そして,gccが,「この計算結果どこにも使われていないから計算自体省いていいよね」という判断をしたようだ.確かにその理論は正しい.
私のコードでも検算の関数をcallするようにしたら,ちゃんとした結果になった.
いままでMIPSアセンブリしか勉強したことないのだが,x86もなんとなく雰囲気は伝わってきたので,アセンブリの勉強をしておいて良かったと感じた出来事だった.
元のコード:約70ms
私のコード:0.0004ms
どう考えてもおかしい.x10とかx100とかならまだ分かるが,元がベンチマークでそれなりに早く動くように書かれているはずなのにx10万はやりすぎだ.こんなに早くステンシルができたらJournalに通ってしまう.
そもそも,
(ループ回数*ループ1回分の処理のステップ数) / (クロック数*CPUのクロック*CPI)
と考えると,キャッシュアクセスやメモリアクセスの時間を差し引いても数msはかかる処理なはずだ.ファイルI/Oなどもない単純なコードなので,そもそもアルゴリズム的には最適化する余地があまりない.コードを見ても,元からほとんどいじっていないので分からない.
不思議なことに,最適化オプションを-O3から-O0にすると,元のコードと同じくらいの「まともな」実行時間になった.
どうやら最適化でゴッソリ何かを削っているようだ.
コンパイルオプションに-save-tempsを付けてアセンブリを読んでみる.
まず,行数が元のコードが1200行ほどなのに,私のものは160行程度であった.
元のコードが同じなのにこれは如何に?
計算部分を見てみる.こういう時にprintfなどのライブラリコールを呼んでおくと良い目印になると思った.
私のコード
call omp_get_wtime
leaq .LC1(%rip), %rcx
movapd %xmm0, %xmm6
movl $548720, %edx
call printf
call omp_get_wtime
元のコード
call omp_get_wtime
movsd .LC4(%rip), %xmm12
movl $10, 340(%rsp)
movapd %xmm0, %xmm10
movq %r13, %r11
movapd %xmm12, %xmm13
.L26:
movq $12800, 248(%rsp)
++400行省略++
leaq .LC5(%rip), %rcx
movq %r11, %r13
movl $548720, %edx
call printf
call omp_get_wtime
コンパイラは賢い.ループ回数を記録するつもりで1ループごとに++していた変数に対しても,いきなりループ回数の合計を定数として突っ込んでprintしている.(下線部)
とりあえず,私のコードでは一番時間のかかるはずの計算部分の約400行がごっそり抜けている.
元のコードではこの計算の後に検算をする関数を読んでいたのだが,ループで計算した結果はその時だけしか使われない.私のコードでは,デバッグのためにその関数のcallを省いていたのが原因だったようだ.そして,gccが,「この計算結果どこにも使われていないから計算自体省いていいよね」という判断をしたようだ.確かにその理論は正しい.
私のコードでも検算の関数をcallするようにしたら,ちゃんとした結果になった.
いままでMIPSアセンブリしか勉強したことないのだが,x86もなんとなく雰囲気は伝わってきたので,アセンブリの勉強をしておいて良かったと感じた出来事だった.
2015年7月10日金曜日
解決法 : `omp_get_wtime' に対する定義されていない参照です R_X86_64_PC32 (未定義シンボル `omp_get_wtime' に対して)
あるベンチマークでのコンパイルエラー
---------------------------------
`omp_get_wtime' に対する定義されていない参照です
再配置がオーバーフローしないように切り詰められました: R_X86_64_PC32 (未定義シンボル `omp_get_wtime' に対して)
---------------------------------
よくあるライブラリのリンクミス.今回はOpenMPが足りない.
コンパイルオプションに-fopenmpを追加すれば解決する.
---------------------------------
`omp_get_wtime' に対する定義されていない参照です
再配置がオーバーフローしないように切り詰められました: R_X86_64_PC32 (未定義シンボル `omp_get_wtime' に対して)
---------------------------------
よくあるライブラリのリンクミス.今回はOpenMPが足りない.
コンパイルオプションに-fopenmpを追加すれば解決する.
解決法: gccのエラー 関数 XX 用の無効な記憶域クラスです invalid storage class for function
Windows7 64bit + Cygwinで姫野ベンチマーク(himenoBMT.c)をgccでコンパイルしようとすると以下のエラーが出る.
-------------------------------
/usr/include/sys/time.h: 関数 ‘second’ 内:
/usr/include/sys/time.h:70:1: エラー: 関数 ‘bintime_addx’ 用の無効な記憶域クラスです
bintime_addx(struct bintime *_bt, uint64_t _x)
^
/usr/include/sys/time.h:81:1: エラー: 関数 ‘bintime_add’ 用の無効な記憶域クラスです
bintime_add(struct bintime *_bt, const struct bintime *_bt2)
^
/usr/include/sys/time.h:93:1: エラー: 関数 ‘bintime_sub’ 用の無効な記憶域クラスです
bintime_sub(struct bintime *_bt, const struct bintime *_bt2)
以下略
-------------------------------
どうやらCygwinはデフォルトでエラーメッセージが日本語らしい.
このままだと検索してもあまり情報が出てこないので,Japanese messages for GNU gccを参照して英語のメッセージを探す.
-------------------------------
/usr/include/sys/time.h: 関数 ‘second’ 内:
/usr/include/sys/time.h:70:1: エラー: 関数 ‘bintime_addx’ 用の無効な記憶域クラスです
bintime_addx(struct bintime *_bt, uint64_t _x)
^
/usr/include/sys/time.h:81:1: エラー: 関数 ‘bintime_add’ 用の無効な記憶域クラスです
bintime_add(struct bintime *_bt, const struct bintime *_bt2)
^
/usr/include/sys/time.h:93:1: エラー: 関数 ‘bintime_sub’ 用の無効な記憶域クラスです
bintime_sub(struct bintime *_bt, const struct bintime *_bt2)
以下略
-------------------------------
どうやらCygwinはデフォルトでエラーメッセージが日本語らしい.
このままだと検索してもあまり情報が出てこないので,Japanese messages for GNU gccを参照して英語のメッセージを探す.
-------------------------------
#: c-decl.c:5930 c-decl.c:5941 c-decl.c:5944
#, gcc-internal-format
msgid "invalid storage class for function %qE"
msgstr "関数 %qE 用の無効な記憶域クラスです"
-------------------------------
英語で検索しても再現しているところはなかった.
とりあえず原因を見に行く.
time.hで
#if __BSD_VISIBLE
以下で呼ばれている部分で詰まっている模様.
この__BSD_VISIBLEは,cdefs.hで定義されている.
#if defined(_ANSI_SOURCE) /* Hide almost everything. */
#define __POSIX_VISIBLE 0
#define __XSI_VISIBLE 0
#define __BSD_VISIBLE 0
#define __ISO_C_VISIBLE 1990
#elif defined(_C99_SOURCE) /* Localism to specify strict C99 env. */
#define __POSIX_VISIBLE 0
#define __XSI_VISIBLE 0
#define __BSD_VISIBLE 0
#define __ISO_C_VISIBLE 1999
#elif defined(_C11_SOURCE) /* Localism to specify strict C11 env. */
#define __POSIX_VISIBLE 0
#define __XSI_VISIBLE 0
#define __BSD_VISIBLE 0
#define __ISO_C_VISIBLE 2011
#else /* Default: everything except __GNU_VISIBLE. */
#define __POSIX_VISIBLE 200809
#define __XSI_VISIBLE 700
#define __BSD_VISIBLE 1
#define __ISO_C_VISIBLE 2011
#endif
おそらくここでDefaultになっており,__BSD_VISIBLEが立っているのだろう.つまり,Default以外にすればよさそうだ.
コンパイルオプションに -D_C99_SOURCE を追加するとコンパイル・実行できるようになった.
2015年7月4日土曜日
Unbox
ブログとTwitterアカウントを作った.
理由はいくつかあるのだが,まずひとつ目は,日本語能力の向上のためだ.例えば,何か物を作る時,難しい物なら私1人が作業するわけではなく他人と協力して作る必要がある.
また,作ったものを流通させる人も,作ったものを使う「顧客」もまた人間だ.
つまり,何かを作ったり売ったりして生活していくには必ず人とコミュニケーションを取る必要がある.
この時,「わかりやすい文章を書ける」というのは非常に大事で,さらに言えばアドバテージになることなのだと思う
理由はいくつかあるのだが,まずひとつ目は,日本語能力の向上のためだ.例えば,何か物を作る時,難しい物なら私1人が作業するわけではなく他人と協力して作る必要がある.
また,作ったものを流通させる人も,作ったものを使う「顧客」もまた人間だ.
つまり,何かを作ったり売ったりして生活していくには必ず人とコミュニケーションを取る必要がある.
この時,「わかりやすい文章を書ける」というのは非常に大事で,さらに言えばアドバテージになることなのだと思う
.
次に問題になるのは,書く題材である.
私は情報系であるし,プログラミングで詰まった部分などの「問題」と「解決方法」を書いて,公開すると世の中にも少し役立つのではないかと思う.
私は情報系であるし,プログラミングで詰まった部分などの「問題」と「解決方法」を書いて,公開すると世の中にも少し役立つのではないかと思う.
そういうわけで,文章を書く練習と技術的なメモを兼ねてブログを書いていくことにした.
Twitterは情報収集のためだ.今どきのエンジニアはブログをやっていない場合がある.持っているのはTwitterとGithubのアカウントだけという場合がある.そういった人からも情報を収集するためにアカウントを作った.また,Twitterは短文投稿という制限があるがゆえに,気軽に書きやすい.最近だと,ブログには「ある程度まとまった量の情報」を書いて,情報共有やちょっとした事項はTwitterのみで済ませるというユーザが多いように感じる.なので,ブログ一本でやっていくより,他のSNSサービスを併用したほうが効率がいいと考えた.
登録:
投稿 (Atom)