Node.js で JavaScript (2015/04/13)
Raspberry Pi で色々なプログラミング言語を試していますが、今回は JavaScript をソースからコンパイルして使ってみます。 JavaScript はブラウザ上で動作する言語としては昔から一般的ですが、サーバ側のプログラミング環境としても Node.js としてメジャーになってきました。Node.js は JavaScriptのJITコンパイラである Google V8 JavaScript Engine を組み込んで、非同期の入出力を行うことが特徴となっている環境です。 また、最近では apt のようなパッケージマネージャを使って、バイナリをインストールするのが普通になってきましたが、ソースファイルをダウンロードして、自分でコンパイルする方法も試してみましょう。 ほとんどのコマンドは「./configure、make、make install」という3つのコマンドを順に実行するだけです。
Node.js は Raspberry Pi の OS である Raspbeian でも「sudo apt-get install nodejs」で簡単にインストールできます。ただし Raspbeian では結構古い v0.6.19 というバージョンになっています。 この記事を書いている時点の最新バージョンは Node,js が v0.12.2 、Node.js からフォーク(枝分かれした) io.js では 1.6.4 というバージョンになっています。
今回は apt-get で簡単にインストールできる Node.js v0.6.19 と簡単にソースからビルドできる Node.js v0.12.2 を使ってみます。 io.js はコンパイルに gcc のバージョンが 4.8 以上が必要なため、gcc 4.6 の Raspbian (Debian Wheezy) 上ではビルドできません。Raspberry Pi で gcc 4.9 のDebian jessie が使えるようになるまで待ちましょう。
apt-get で Node.js をインストール
Raspbian では Node.js は nodejs というパッケージになっています。
$ sudo apt-get install nodejs
[sudo] password for jun:
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
libc-ares2 libev4 libv8-3.8.9.20
The following NEW packages will be installed:
libc-ares2 libev4 libv8-3.8.9.20 nodejs
0 upgraded, 4 newly installed, 0 to remove and 2 not upgraded.
Need to get 2,122 kB of archives.
After this operation, 5,652 kB of additional disk space will be used.
Do you want to continue [Y/n]? y
:
略
:
コマンドも nodejs というファイル名になります。
$ ls -l /usr/bin/nodejs -rwxr-xr-x 1 root root 1213028 Dec 23 2012 /usr/bin/nodejs $ ldd /usr/bin/nodejs /usr/lib/arm-linux-gnueabihf/libcofi_rpi.so (0x76f73000) libz.so.1 => /lib/arm-linux-gnueabihf/libz.so.1 (0x76f45000) librt.so.1 => /lib/arm-linux-gnueabihf/librt.so.1 (0x76f36000) libssl.so.1.0.0 => /usr/lib/arm-linux-gnueabihf/libssl.so.1.0.0 (0x76ee5000) libcrypto.so.1.0.0 => /usr/lib/arm-linux-gnueabihf/libcrypto.so.1.0.0 (0x76d81000) libcares.so.2 => /usr/lib/arm-linux-gnueabihf/libcares.so.2 (0x76d6b000) libev.so.4 => /usr/lib/libev.so.4 (0x76d57000) libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0x76d4c000) libutil.so.1 => /lib/arm-linux-gnueabihf/libutil.so.1 (0x76d41000) libv8.so.3.8.9.20 => /usr/lib/libv8.so.3.8.9.20 (0x76a65000) libstdc++.so.6 => /usr/lib/arm-linux-gnueabihf/libstdc++.so.6 (0x76993000) libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0x76921000) libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0x768f9000) libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0x768da000) libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x767a9000) /lib/ld-linux-armhf.so.3 (0x76f80000) $ file /usr/bin/nodejs /usr/bin/nodejs: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0x6bc14736fc91b736c44917ec7edbba0061cbbdec, stripped
リンクされている共有ライブラリのバージョンを見てみると、JavaScriptのJITコンパイラである Google V8 JavaScript Engine のバージョンが 3.8.9.20 になっているのが分かります。
Node.js のバージョンを確認してみます。
$ nodejs -v
v0.6.19
https://nodejs.org/dist/v0.6.19/ を見ると 2012/06/06に公開されたバージョンのようです。 最初のバージョン (v0.01 2009/05/27) が6年前に出てきた Node.js にとって、3年前のバージョンではあまりに古すぎます。
最新版の Node.js のビルド
2015/04/12 現在の安定版の最新は https://nodejs.org/dist/v0.12.2/ にある 2015/03/31 Node.js v0.12.2 でした。v8 エンジンは 3.28.73 となっています。 Raspberry Pi2 上でビルドしてみます。普通に ./configure、make、make install を順に実行するだけと簡単です。ただし Raspberry Pi2 でも45分ほどの時間がかかります。 以下の実行例では、コマンドの先頭に 「time」を付けて実行に費やした時間がわかるようにしていますが、time がなくても大丈夫です。
ダウンロード
$ wget https://nodejs.org/dist/v0.12.2/node-v0.12.2.tar.gz $ wget https://nodejs.org/dist/v0.12.2/node-v0.12.2.tar.gz --2015-04-11 01:38:31-- https://nodejs.org/dist/v0.12.2/node-v0.12.2.tar.gz Resolving nodejs.org (nodejs.org)... 165.225.133.150 Connecting to nodejs.org (nodejs.org)|165.225.133.150|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 19311976 (18M) [application/octet-stream] Saving to: `node-v0.12.2.tar.gz' 100%[======================================>] 19,311,976 2.73M/s in 7.7s 2015-04-11 01:38:39 (2.40 MB/s) - `node-v0.12.2.tar.gz' saved [19311976/19311976]
ファイルの確認
ダウンロードしたファイルのハッシュ値を確認します。
$ shasum node-v0.12.2.tar.gz a969f17a0a6c9238584f8946d96e8d39be8eb957 node-v0.12.2.tar.gz
正しい値は以下のファイルに記載されています。
https://nodejs.org/dist/v0.12.2/SHASUMS.txt
a969f17a0a6c9238584f8946d96e8d39be8eb957 node-v0.12.2.tar.gz
一致しているので正しくダウンロードできたことが確認できます。
圧縮ファイルの展開
$ time tar zxf node-v0.12.2.tar.gz
real 0m10.280s
user 0m4.980s
sys 0m4.190s
configure
$ cd node-v0.12.2 $ time ./configure creating ./icu_config.gypi { 'target_defaults': { 'cflags': [], 'default_configuration': 'Release', 'defines': [], 'include_dirs': [], 'libraries': []}, 'variables': { 'arm_float_abi': 'hard', 'arm_fpu': 'vfpv3', 'arm_neon': 0, 'arm_thumb': 0, 'arm_version': '6', 'clang': 0, 'gcc_version': 46, 'host_arch': 'arm', 'icu_small': 'false', 'node_install_npm': 'true', 'node_prefix': , 'node_shared_cares': 'false', 'node_shared_cares': 'false', 'node_shared_http_parser': 'false', 'node_shared_libuv': 'false', 'node_shared_openssl': 'false', 'node_shared_v8': 'false', 'node_shared_zlib': 'false', 'node_tag': , 'node_use_dtrace': 'false', 'node_use_dtrace': 'false', 'node_use_etw': 'false', 'node_use_mdb': 'false', 'node_use_openssl': 'true', 'node_use_perfctr': 'false', 'openssl_no_asm': 0, 'python': '/usr/bin/python', 'target_arch': 'arm', 'uv_library': 'static_library', 'uv_parent_path': '/deps/uv/', 'uv_use_dtrace': 'false', 'v8_enable_gdbjit': 0, 'v8_enable_i18n_support': 0, 'v8_no_strict_aliasing': 1, 'v8_optimized_debug': 0, 'v8_random_seed': 0, 'v8_use_snapshot': 'true', 'want_separate_host_toolset': 0}} creating ./config.gypi creating ./config.mk real 0m20.934s user 0m5.040s sys 0m0.600s
make
Raspberry Pi2 は4つのCPUコアを持っているので、並列度 4 (-j 4) でコンパイルしました。
$ time make -j 4
:
略
:
real 45m19.912s
user 151m51.100s
sys 7m57.740s
45分でコンパイル終了です。並列でコンパイルするとコア数が4つの威力が発揮されます。
Raspberry Pi2 のCPUクロックはヒマなときは 600MHzで動作します。 コンパイル中のCPUクロックを確認すると、ちゃんと 900MHz で動作していました。
$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
900000
コンパイル中のCPUの温度は51度でした。
$ /bin/cat /sys/class/thermal/thermal_zone0/temp
50843
並列コンパイルを指定しない場合も試してみましたが、2時間かかりました。
$ time make
:
楽
:
real 124m38.276s
user 113m33.000s
sys 5m35.940s
インストール
インストールは次のコマンドで /usr/local/bin/ フォルダ以下にインストールされます。
$ sudo make install
apt-get でインストールすると /usr/bin/ フォルダ以下に格納されるため、衝突しません。 またコマンド名が apt-get では nodejs、自分でビルドすると node となっているので、実行する場合も使い分けることができます。
$ ls -lt /usr/local/bin | head -n 3 total 15932 lrwxrwxrwx 1 root staff 38 Apr 11 22:23 npm -> ../lib/node_modules/npm/bin/npm-cli.js -rwxr-xr-x 1 root staff 14267584 Apr 11 22:18 node $ du /usr/local/include/node 8 /usr/local/include/node/libplatform 16 /usr/local/include/node/openssl 736 /usr/local/include/node $ du --max-depth=2 /usr/local/lib/node_modules/ 600 /usr/local/lib/node_modules/npm/man 32 /usr/local/lib/node_modules/npm/bin 1512 /usr/local/lib/node_modules/npm/html 8928 /usr/local/lib/node_modules/npm/node_modules 44 /usr/local/lib/node_modules/npm/scripts 588 /usr/local/lib/node_modules/npm/lib 528 /usr/local/lib/node_modules/npm/doc 12452 /usr/local/lib/node_modules/npm 12456 /usr/local/lib/node_modules/
strip
作成した node コマンドから実行に不要なシンボルを削除して、バイナリのサイズを縮めてみます。
$ sudo strip /usr/local/bin/node $ ls -lt /usr/local/bin | head -n 2 total 12796 -rwxr-xr-x 1 root staff 11058088 Apr 12 01:43 node
すこし小さくなりました。
バイナリの確認
ビルドしたバイナリの情報を見てみましょう。
リンクされているライブラリ
node コマンドにダイナミックリンクされているライブラリを確認します。
$ ldd /usr/local/bin/node
/usr/lib/arm-linux-gnueabihf/libcofi_rpi.so (0x76ef7000)
librt.so.1 => /lib/arm-linux-gnueabihf/librt.so.1 (0x76ed8000)
libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0x76ecd000)
libstdc++.so.6 => /usr/lib/arm-linux-gnueabihf/libstdc++.so.6 (0x76dfb000)
libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0x76d8a000)
libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0x76d62000)
libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0x76d42000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x76c11000)
/lib/ld-linux-armhf.so.3 (0x76f04000)
ファイルの情報
file コマンドでも node コマンドのバイナリの情報を確認できます。ARM の32ビット版のELF形式のバイナリでダイナミックリンクしたライブラリを使っていて、シンボルをストリップ済みであることが確認できます。
$ file /usr/local/bin/node
/usr/local/bin/node: ELF 32-bit LSB executable, ARM, version 1 (GNU/Linux), dynamically linked (uses shared libs),
for GNU/Linux 2.6.26, BuildID[sha1]=0x7fb7702479a6c3d2a945e5bc75196d0bac0d81f7, stripped
ベンチマークの実行
いつものように 4x4行列の乗算を100万回計算する時間を測定してみます。Node.js の特徴である非同期入出力を使うこともなく、普通にコマンドラインから使うスクリプト言語として使っています。 実行速度に関して、Node.js のバージョンの違いというよりは、組み込まれている Google V8 JavaScript Engine のバージョンの違いで速度が異なっているものと思われます。
CPUクロックの固定
Raspberry Pi2 のCPUクロックは、アイドル時は600MHz、忙しくなると900MHzに切り替わります。ベンチマークの途中でクロックが切り替わると、よくわからない結果になるので、ここでは900MHzに固定します。
$ sudo su # echo "performance" >/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor # exit $ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 900000
元の動的に切り替わるように戻すには以下のコマンドで「ondemand」に設定します。
$ sudo su # echo "ondemand" >/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor # exit $ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 600000
コード その1
// matrixbench.js function mul(a, b) { var a0 =a[ 0], a1 =a[ 1], a2 =a[ 2], a3 =a[ 3], a4 =a[ 4], a5 =a[ 5], a6 =a[ 6], a7 =a[ 7], a8 =a[ 8], a9 =a[ 9], a10=a[10], a11=a[11], a12=a[12], a13=a[13], a14=a[14], a15=a[15]; var b0 =b[ 0], b1 =b[ 1], b2 =b[ 2], b3 =b[ 3], b4 =b[ 4], b5 =b[ 5], b6 =b[ 6], b7 =b[ 7], b8 =b[ 8], b9 =b[ 9], b10=b[10], b11=b[11], b12=b[12], b13=b[13], b14=b[14], b15=b[15]; var r = new Array(16); r[ 0] = a0 * b0 + a4 * b1 + a8 * b2 + a12 * b3; r[ 1] = a1 * b0 + a5 * b1 + a9 * b2 + a13 * b3; r[ 2] = a2 * b0 + a6 * b1 + a10 * b2 + a14 * b3; r[ 3] = a3 * b0 + a7 * b1 + a11 * b2 + a15 * b3; r[ 4] = a0 * b4 + a4 * b5 + a8 * b6 + a12 * b7; r[ 5] = a1 * b4 + a5 * b5 + a9 * b6 + a13 * b7; r[ 6] = a2 * b4 + a6 * b5 + a10 * b6 + a14 * b7; r[ 7] = a3 * b4 + a7 * b5 + a11 * b6 + a15 * b7; r[ 8] = a0 * b8 + a4 * b9 + a8 * b10+ a12 * b11; r[ 9] = a1 * b8 + a5 * b9 + a9 * b10+ a13 * b11; r[10] = a2 * b8 + a6 * b9 + a10 * b10+ a14 * b11; r[11] = a3 * b8 + a7 * b9 + a11 * b10+ a15 * b11; r[12] = a0 * b12+ a4 * b13+ a8 * b14+ a12 * b15; r[13] = a1 * b12+ a5 * b13+ a9 * b14+ a13 * b15; r[14] = a2 * b12+ a6 * b13+ a10 * b14+ a14 * b15; r[15] = a3 * b12+ a7 * b13+ a11 * b14+ a15 * b15; return r; }; function fix4(f) { var i; var str = f.toFixed(4); var len = 10 - str.length; if (len > 0) { for (i=0; i<len; i++) { str = " " + str; } } return str; }; function print_console(m) { return console.log(fix4(m[0]) + " " + fix4(m[4]) + " " + fix4(m[8]) + " " + fix4(m[12]) + "\n" + fix4(m[1]) + " " + fix4(m[5]) + " " + fix4(m[9]) + " " + fix4(m[13]) + "\n" + fix4(m[2]) + " " + fix4(m[6]) + " " + fix4(m[10]) + " " + fix4(m[14]) + "\n" + fix4(m[3]) + " " + fix4(m[7]) + " " + fix4(m[11]) + " " + fix4(m[15]) + "\n"); }; a = [ 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 ]; b = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ]; var r; console.time("time"); for (i=0; i<1000000; i++) { r = mul(a, b); } console.timeEnd("time"); print_console(r);
Node.js v0.12.2 (2015/03/31)
新しいバージョンの Node.js v0.12.2 の実行結果です。
jun@raspberrypi ~ $ node -v v0.12.2 $ time node matrixbench.js $ time node matrixbench.js time: 1301ms 6.0000 22.0000 38.0000 54.0000 12.0000 44.0000 76.0000 108.0000 18.0000 66.0000 114.0000 162.0000 24.0000 88.0000 152.0000 216.0000 real 0m1.717s user 0m1.670s sys 0m0.050s
Java8 や LuaJIT よりは少し遅いですが、十分に高速です。
$ time java mulMatrix
Estimated Time = 0.75181697 sec
6.00000e+00 2.20000e+01 3.80000e+01 5.40000e+01
1.20000e+01 4.40000e+01 7.60000e+01 1.08000e+02
1.80000e+01 6.60000e+01 1.14000e+02 1.62000e+02
2.40000e+01 8.80000e+01 1.52000e+02 2.16000e+02
real 0m1.295s
user 0m1.310s
sys 0m0.060s
Node.js v0.6.19 (2012/06/06)
古いバージョンでは5倍遅くなりますが、それでもNumPy を使った Python のRaspberry Pi2の結果と比べても2倍の速度です。
$ nodejs -v v0.6.19 $ nodejs matrixbench.js time: 6718ms 6.0000 22.0000 38.0000 54.0000 12.0000 44.0000 76.0000 108.0000 18.0000 66.0000 114.0000 162.0000 24.0000 88.0000 152.0000 216.0000
コード その2
JavaScript の書き方を変えて、配列を直接参照する方法です。配列の要素を単純変数にキャッシュするほうが速いか、変数にコピーする時間が無駄になるかを確認しています。
// matrixbench2.js function mul(a, b) { var r = new Array(16); r[ 0] = a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3]; r[ 1] = a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3]; r[ 2] = a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3]; r[ 3] = a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3]; r[ 4] = a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7]; r[ 5] = a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7]; r[ 6] = a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7]; r[ 7] = a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7]; r[ 8] = a[0] * b[8] + a[4] * b[9] + a[8] * b[10]+ a[12] * b[11]; r[ 9] = a[1] * b[8] + a[5] * b[9] + a[9] * b[10]+ a[13] * b[11]; r[10] = a[2] * b[8] + a[6] * b[9] + a[10] * b[10]+ a[14] * b[11]; r[11] = a[3] * b[8] + a[7] * b[9] + a[11] * b[10]+ a[15] * b[11]; r[12] = a[0] * b[12]+ a[4] * b[13]+ a[8] * b[14]+ a[12] * b[15]; r[13] = a[1] * b[12]+ a[5] * b[13]+ a[9] * b[14]+ a[13] * b[15]; r[14] = a[2] * b[12]+ a[6] * b[13]+ a[10] * b[14]+ a[14] * b[15]; r[15] = a[3] * b[12]+ a[7] * b[13]+ a[11] * b[14]+ a[15] * b[15]; return r; }; function fix4(f) { var i; var str = f.toFixed(4); var len = 10 - str.length; if (len > 0) { for (i=0; i<len; i++) { str = " " + str; } } return str; }; function print_console(m) { return console.log(fix4(m[0]) + " " + fix4(m[4]) + " " + fix4(m[8]) + " " + fix4(m[12]) + "\n" + fix4(m[1]) + " " + fix4(m[5]) + " " + fix4(m[9]) + " " + fix4(m[13]) + "\n" + fix4(m[2]) + " " + fix4(m[6]) + " " + fix4(m[10]) + " " + fix4(m[14]) + "\n" + fix4(m[3]) + " " + fix4(m[7]) + " " + fix4(m[11]) + " " + fix4(m[15]) + "\n"); }; a = [ 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 ]; b = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ]; var r; console.time("time"); for (i=0; i<1000000; i++) { r = mul(a, b); } console.timeEnd("time"); print_console(r);
Node.js v0.6.19
倍近く時間がかかります。配列のアクセスに時間がかかっている模様です。
$ nodejs matrixbench2.js
time: 12244ms
6.0000 22.0000 38.0000 54.0000
12.0000 44.0000 76.0000 108.0000
18.0000 66.0000 114.0000 162.0000
24.0000 88.0000 152.0000 216.0000
Node.js v0.12.2
新しいバージョンでは10倍近く高速化されています。
$ node matrixbench2.js
time: 1303ms
6.0000 22.0000 38.0000 54.0000
12.0000 44.0000 76.0000 108.0000
18.0000 66.0000 114.0000 162.0000
24.0000 88.0000 152.0000 216.0000
ベンチマークまとめ
Raspberry Pi2 でのベンチマークのまとめです。各言語のコードは こちら と こちら です。今回はCPUクロックを900MHzに固定しているので、ばらつきが少なくなっていると思います。
言語 | 備考 | 実行時間(秒) |
---|---|---|
Node.js | v0.12.2 | 1.34 |
v0.6.19 | 6.72 | |
Java 1.8.0 | - | 0.75 |
C# | mono 3.12.1 | 1.22 |
Python 2.7.3 | リスト | 56.53 |
NumPy | 14.57 | |
Python 3.2.3 | リスト | 59.95 |
NumPy | 16.15 | |
Lua 5.1.5 | - | 11.87 |
LuaJIT 2.0.0 | テーブル(JIT) | 0.88 |
テーブル(JIT OFF) | 5.09 | |
ffi double (JIT) | 0.63 | |
ffi double (JIT OFF) | 32.05 | |
gcc 4.6.3 | -O0 | 1.18 |
-O2 | 0.41 | |
アセンブリ | ベクトル演算 | 41.61 |
アセンブリ | NEON (最適化後) | 0.13 |
LuaJIT、Java、JavaScript、C# といった JITコンパイルを行う言語はどれも2秒以下で実行できていて、Cとあまり変わらないことが分かります。
【おまけ】C# のソース
Raspberry Pi で C# も使う事ができます。使ったソースは以下のものです。
using System; public class Matrix { static double[] A = new double[16] {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4}; static double[] B = new double[16] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; static double[] C = new double[16] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; public static void mulArrayMatrix(double[] mr, double[] ma, double[] mb) { mr[ 0] = ma[0] * mb[0] + ma[4] * mb[1] + ma[8] * mb[2] + ma[12] * mb[3]; mr[ 1] = ma[1] * mb[0] + ma[5] * mb[1] + ma[9] * mb[2] + ma[13] * mb[3]; mr[ 2] = ma[2] * mb[0] + ma[6] * mb[1] + ma[10] * mb[2] + ma[14] * mb[3]; mr[ 3] = ma[3] * mb[0] + ma[7] * mb[1] + ma[11] * mb[2] + ma[15] * mb[3]; mr[ 4] = ma[0] * mb[4] + ma[4] * mb[5] + ma[8] * mb[6] + ma[12] * mb[7]; mr[ 5] = ma[1] * mb[4] + ma[5] * mb[5] + ma[9] * mb[6] + ma[13] * mb[7]; mr[ 6] = ma[2] * mb[4] + ma[6] * mb[5] + ma[10] * mb[6] + ma[14] * mb[7]; mr[ 7] = ma[3] * mb[4] + ma[7] * mb[5] + ma[11] * mb[6] + ma[15] * mb[7]; mr[ 8] = ma[0] * mb[8] + ma[4] * mb[9] + ma[8] * mb[10]+ ma[12] * mb[11]; mr[ 9] = ma[1] * mb[8] + ma[5] * mb[9] + ma[9] * mb[10]+ ma[13] * mb[11]; mr[10] = ma[2] * mb[8] + ma[6] * mb[9] + ma[10] * mb[10]+ ma[14] * mb[11]; mr[11] = ma[3] * mb[8] + ma[7] * mb[9] + ma[11] * mb[10]+ ma[15] * mb[11]; mr[12] = ma[0] * mb[12]+ ma[4] * mb[13]+ ma[8] * mb[14]+ ma[12] * mb[15]; mr[13] = ma[1] * mb[12]+ ma[5] * mb[13]+ ma[9] * mb[14]+ ma[13] * mb[15]; mr[14] = ma[2] * mb[12]+ ma[6] * mb[13]+ ma[10] * mb[14]+ ma[14] * mb[15]; mr[15] = ma[3] * mb[12]+ ma[7] * mb[13]+ ma[11] * mb[14]+ ma[15] * mb[15]; } public static void printArrayMatrix4(double[] ma) { Console.WriteLine(" {0:e5} {1:e5} {2:e5} {3:e5}", ma[0], ma[4], ma[8], ma[12]); Console.WriteLine(" {0:e5} {1:e5} {2:e5} {3:e5}", ma[1], ma[5], ma[9], ma[13]); Console.WriteLine(" {0:e5} {1:e5} {2:e5} {3:e5}", ma[2], ma[6], ma[10], ma[14]); Console.WriteLine(" {0:e5} {1:e5} {2:e5} {3:e5}", ma[3], ma[7], ma[11], ma[15]); } public static void Main(string[] args) { System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); for (int i=0; i<1000000; i++) { mulArrayMatrix(C, A, B); } sw.Stop(); printArrayMatrix4(C); Console.WriteLine(sw.Elapsed); } }