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);
  }
}


続く...



このページの目次