色々な言語でフィボナッチ
なんとなく色々な言語でフィボナッチワンライナーを実装してみました。
Ruby
p 0; a = 1; b = 0; while a < 10000 do p a; a += b; b = a - b; end
Perl
print 0; $a = 1; $b = 0; while ($a < 10000) {print "$a\n"; $a += $b; $b = $a - $b}
Python
a = 1; b= 0 while < 10000: print str(a); a+= b; b= a- b
Pythonがうまく一行に出来なかったので誰か教えてください。なんとなくgenerator使わないといけない気がする。。。
2GBを超えるメモリを1つのプロセスで確保したい場合
C++で大量の変数を使って計算したい時に、たくさん変数確保するようなことがあるかと思います。 例えば以下のようなコードとか
const int REPEAT_TIMES = 10000; double num_a[REPEAT_TIMES][REPEAT_TIMES], num_b[REPEAT_TIMES][REPEAT_TIMES], num_c[REPEAT_TIMES][REPEAT_TIMES]; double num_d[REPEAT_TIMES][REPEAT_TIMES], num_e[REPEAT_TIMES][REPEAT_TIMES], num_f[REPEAT_TIMES][REPEAT_TIMES]; double num_g[REPEAT_TIMES][REPEAT_TIMES], num_h[REPEAT_TIMES][REPEAT_TIMES], num_i[REPEAT_TIMES][REPEAT_TIMES]; double num_j[REPEAT_TIMES][REPEAT_TIMES], num_k[REPEAT_TIMES][REPEAT_TIMES], num_l[REPEAT_TIMES][REPEAT_TIMES]; double num_m[REPEAT_TIMES][REPEAT_TIMES], num_n[REPEAT_TIMES][REPEAT_TIMES], num_o[REPEAT_TIMES][REPEAT_TIMES]; double num_p[REPEAT_TIMES][REPEAT_TIMES], num_q[REPEAT_TIMES][REPEAT_TIMES], num_r[REPEAT_TIMES][REPEAT_TIMES];
まあウンコードなのは気にしないでくださいw
しかしこのようなコードを書いて、いざコンパイルしようとした時次のようなエラーが出てくることがあります
/tmp/ccEo3zaI.o: In function `_Z11calc_vectorv.omp_fn.2': calcMultiThreadManyDataDouble.cpp:(.text+0x3ba): relocation truncated to fit: R_X86_64_32S against symbol `num_d' defined in .bss section in /tmp/ccEo3zaI.o calcMultiThreadManyDataDouble.cpp:(.text+0x3e7): relocation truncated to fit: R_X86_64_32S against symbol `num_e' defined in .bss section in /tmp/ccEo3zaI.o calcMultiThreadManyDataDouble.cpp:(.text+0x414): relocation truncated to fit: R_X86_64_32S against symbol `num_f' defined in .bss section in /tmp/ccEo3zaI.o calcMultiThreadManyDataDouble.cpp:(.text+0x441): relocation truncated to fit: R_X86_64_32S against symbol `num_g' defined in .bss section in /tmp/ccEo3zaI.o calcMultiThreadManyDataDouble.cpp:(.text+0x46e): relocation truncated to fit: R_X86_64_32S against symbol `num_h' defined in .bss section in /tmp/ccEo3zaI.o calcMultiThreadManyDataDouble.cpp:(.text+0x49b): relocation truncated to fit: R_X86_64_32S against symbol `num_i' defined in .bss section in /tmp/ccEo3zaI.o calcMultiThreadManyDataDouble.cpp:(.text+0x4c8): relocation truncated to fit: R_X86_64_32S against symbol `num_j' defined in .bss section in /tmp/ccEo3zaI.o calcMultiThreadManyDataDouble.cpp:(.text+0x4f5): relocation truncated to fit: R_X86_64_32S against symbol `num_k' defined in .bss section in /tmp/ccEo3zaI.o calcMultiThreadManyDataDouble.cpp:(.text+0x522): additional relocation overflows omitted from the output collect2: ld returned 1 exit status
relocation truncated to fit
再配置はサイズに合うように切り捨てられました??ん??どういうことだ?
色々調べていたら次のサイトに巡りあうことが出来ました
つまるところ、64ビットコンパイラでは、デフォルトでは32ビットと互換性を保つために2GB制限をかけているけど、コンパイルオプションを追加してやることで64ビットアプリケーションとしてコンパイル出来るようになるようです
よってコンパイル時に
$ g++ ファイル名.cpp -o 出力したい名前 -mcmodel=large
としてやればコンパイル出来るようになります。
実行結果を見てみると
この計算機はメモリが24GB積んであるのでこのプロセスだけで13.7GB程度消費しています。 …やばいな笑
CPUで並列処理
今回はGPU使ってないのでこの画像は適切じゃないんだけど、ちょっと適当なの探すのが面倒だったのでw
C++でThread並列を実装してみました。といっても大層なことをしたわけではなく、OpenMPを利用しただけです。 そもそもOPenMPってなんぞやってかんじですよね。すごい雑にいうと、マルチスレッド対応の実行ファイルを生成するのを補助してくれる業界規格です。この辺の詳しい事に関しては、以下の資料を読むとわかりやすかったです
利用方法はとても簡単で、omp.h
ヘッダファイルをインクルードした上で、#pragma`ではじまるディレクティブをforやwhileの前に書いてあげるだけです
といってもコードを書かないと何言ってるんだってなると思われるので、まずシングルスレッド版(並列化していないバージョン)を示します
#include <iostream> #include <string> #include <stdlib.h> const int REPEAT_TIMES = 10000; unsigned int num_a[REPEAT_TIMES][REPEAT_TIMES], num_b[REPEAT_TIMES][REPEAT_TIMES], num_c[REPEAT_TIMES][REPEAT_TIMES]; void initialize_params() { for (int counter = 0; counter < REPEAT_TIMES; counter++) { for (int counter2 = 0; counter2 < REPEAT_TIMES; counter2++) { num_a[counter][counter2] = 1 * counter2; num_b[counter][counter2] = 2 * counter; } } } int calc_vector() { for (int counter = 0; counter < REPEAT_TIMES; counter++) { for (int counter2 = 0; counter2 < REPEAT_TIMES; counter2++) { num_c[counter][counter2] = num_a[counter][counter2] + num_b[counter][counter2]; } } } int main (int argc, char** argv) { double measure_start, measure_stop; measure_start = clock(); for (int counter = 0; counter <= 100; counter++) { initialize_params(); calc_vector(); } measure_stop = clock(); std::cout << "処理時間" << (double)(measure_stop - measure_start) / CLOCKS_PER_SEC << "[s]" << std::endl; return 0; }
まあ至って単純。ただの足し算をひたすらしているだけです。これを並列化するためには
#include <omp.h>
を新たに付け足し、適切な箇所でディレクティブを追記してあげればOK。
すると次のようになるかと思われます
#include <iostream> #include <string> #include <stdlib.h> #include <omp.h> //OpenMPを用いたCPU並列化バージョン using namespace std; const int REPEAT_TIMES = 10000; unsigned int num_a[REPEAT_TIMES][REPEAT_TIMES], num_b[REPEAT_TIMES][REPEAT_TIMES], num_c[REPEAT_TIMES][REPEAT_TIMES]; void initialize_params() { #pragma omp parallel for for (int counter = 0; counter < REPEAT_TIMES; counter++) { #pragma omp parallel for for (int counter2 = 0; counter2 < REPEAT_TIMES; counter2++) { num_a[counter][counter2] = 1 * counter2; num_b[counter][counter2] = 2 * counter; } } } int calc_vector() { #pragma omp parallel for for (int counter = 0; counter < REPEAT_TIMES; counter++) { #pragma omp parallel for for (int counter2 = 0; counter2 < REPEAT_TIMES; counter2++) { num_c[counter][counter2] = num_a[counter][counter2] + num_b[counter][counter2]; } } } int main (int argc, char** argv) { time_t start_time, stop_time; time(&start_time); #pragma omp parallel for for (int counter = 0; counter <= 100; counter++) { initialize_params(); calc_vector(); } time(&stop_time); cout << "処理時間" << (double)(stop_time - start_time) << "[s]" << endl; return 0; }
今回はCentOS上で実行するので、コンパイルオプションに-fopenmp
をつけてコンパイルしました
$ g++ ファイル名.cpp -o 出力したい名前 -fopenmp
これでコンパイルし、MacBookPro上に立てたVagrantで実行しました。
結果は以下の表の通りになりました
シングルスレッド: 53.9202[s]
マルチスレッド(4スレッド): 16.0[s]
おー。大体3.3倍くらい早くなっていますね。4倍とはいかないものの、効果は絶大です
すべての処理を並列化出来るわけではないですが(時間発展するやつとかはそこまで早くならない)、積極的にマルチスレッドを活用していきたいと思いました
PlayFrameworkをちょっと触った
PlayFrameworkを触る機会があったので、自分の備忘録がてら書きます 以下の操作はUbuntu上で実行しています
皆さんは知っていると思いますが、PlayFrameworkはScala(と一部Java)で実装されています。よって利用するにはJavaが必要になります。Ubuntuインストール時のオプションにも依りますが、Javaが入っていないこともあるので、まずはJavaをインストールします
PlayFrameworkの最新バージョンは、この記事を書いた時には2.4.2だったので、必要なJDKのバージョンは8でした。よってJDK8を入れます
$ sudo add-apt-repository ppa:webupd8team/java $ sudo apt-get update $ sudo apt-get install oracle-java8-installer $ sudo apt-get install oracle-java8-set-default
次にPlayFramework本体をダウンロードしてきます。今回は本家のサイトからダウンロードしてくる形にしました
$ cd $HOME $ wget "https://downloads.typesafe.com/typesafe-activator/1.3.5/typesafe-activator-1.3.5.zip" $ unzip typesafe-activator-1.3.5.zip
実行ファイルに対してパスが通っていないので、環境変数に追記
export PATH=/home/ユーザー名/activator-dist-1.3.5/activator:$PATH
動作チェックを兼ねて、以下のコマンドで新規にPlayアプリケーションを作成します
$ activator new SampleApp
するとSampleApp
というフォルダが出来たと思うの、移動して、以下のコマンドで実行します。初回起動はBuildが走るため、ちょっと時間がかかります
$ activator run
ブラウザからlocalhots:9000
にアクセスして、Your new application is readyと表示されれば正常に稼働しています
雑記
いやー全然書いてねえなw
帰省している間はほとんどパソコンを開かなかったので、結局何も進みませんでしたとさ。まあ休みに行っているからそれでいいんだけど
OpenMPで並列化しようとしてうまく行かなかったりRubyの並列処理書き途中だったりして、色々と中途半端だったのですが、それよりも自分の部屋が最悪に汚くなって、完全に作業効率落ちていたので、今日はちょっと重めに掃除しました(大掃除ではないけど、いつもの掃除機かけるだけよりは上な感じで)
まずたくさん積まれた酒瓶を片付け、ビールの空き缶を片付け、ミネラルウォーターのペットボトルを捨てたらエライ綺麗になりました。つーかこまめに捨てよろって話ですね。すみません
さらにもう使わないであろう教科書やプリント類を捨てたり、ダンボールにまとめたら山みたいなのが消失しました。もっと早くにやればよかった。。。
僕の部屋はまだまだものが多いのですが、まあ少しずつ捨てるなり譲るなりしないとなと思っています。どうせ来年には引っ越すわけだし
というわけで久しぶりの更新でした