最適化オプションを知った。
言語の処理速度に興味があったので、C言語・Java・phpで同じ処理を行った場合の速度比較をやってみた。
処理内容は、以下サイトのコードとほぼ同じ。
C言語
#include <stdio.h> int main(int argc, char *argv[]) { int rep = 1000000; // 足し算の回数 int n = 10000; // 変数の数(配列の要素数) int x[10000] = {}; for (int j = 0; j < rep; ++j) { for (int i = 0; i < n; ++i) { x[i] += 1; } } }
Java
package com.company; import java.util.Arrays; public class Main { public static void main(String[] args) { int rep = 1000000; int n = 10000; int[] x = new int[n]; Arrays.fill(x, 0); for (int j = 0; j < rep;) { for (int i = 0; i < n;) { x[i] += 1; i++; } j++; } } }
PHP
<?php $rep = 1000000; $n = 10000; $x = range(0,$n); for($j = 1; $j < $rep; $j++){ for($i = 0; $i < $n-1; $i++){ $x[$i] += $i; } } ?>
結果
ざっくりと知りたかったので、 今回はtimeコマンドでやってみました。
C言語 | Java | PHP | |
---|---|---|---|
real | 0m26.795s | 0m1.698s | 9m54.769s |
user | 0m26.754s | 0m2.134s | 9m53.398s |
sys | 0m0.028s | 0m0.217s | 0m0.789s |
様子がおかしい?
PHPは、そうなんだろうなという印象だったのだけど、 CとJavaが思ってたのと違う!!
CとJavaについては、まったくいじったこと無かったので、 よくわからないので、質問してみました。
c言語、javaの実行速度について、解決できない疑問があります。 … - 人力検索はてな
ご回答頂いた方によると、コンパイラの最適化が関連しているのでは?ということ。調べてみる。
最適化実行
C言語をコンパイルするときは、自分のMac環境では以下の様に行っていました。
gcc main.c -o main.out
これを以下の様にオプションをつけると、 どうなるか。
gcc -O2 main.c -o main.out
再度計測!
C言語 (最適化前) | C言語 (最適化後) | Java | PHP | |
---|---|---|---|---|
real | 0m26.795s | 0m1.484s | 0m1.698s | 9m54.769s |
user | 0m26.754s | 0m1.474s | 0m2.134s | 9m53.398s |
sys | 0m0.028s | 0m0.006s | 0m0.217s | 0m0.789s |
これだよ、求めていたのは!
最適化オプションをつけたとき、コンパイラは何をしているんだろう。
最適化については、コンパイラによって様々な最適化を行うらしいのだけど、 共通しているのは、コンパイルにコストをかけて、プログラム実行時のコストを減らすということみたい。
Using and Porting the GNU Compiler Collection (GCC) - GCCコマンド・オプション
今回のコードにおいては、最適化オプション指定時に変数へのアクセスが、メモリじゃなくて、CPUにあるレジスタにおきかわって、forとかでアクセスしまくっている変数がregister宣言に置き換わって、爆速になったのでは?と考えている。
最適化結果を知りたいと思って、逆アセンブラとかしてみたんだけど、 アセンブラが読めないことを忘れていて、撃沈。。
結論
自分のような、WEBのフロント側から、バックにきた人間にとっては、 C言語のコンパイルの裏、レジスタのことなど、 当たり前だけど、知らないことが多いので、こうやって調べていくのは大変勉強になる。
普段phpのWEBアプリを作っていて、 たまに複雑な計算処理などが入るときも 力業でphpで書いて、夜間にバッチで処理なんてやってたけど、 ある部分、処理にあった言語で実装をしなおして 高速化や効率化を今後考えていきたい。
追い切れていないところ
- JavaのJITコンパイルは、どうして実行のときにコンパイルするのに、処理が高速化できんの? 自分の感覚としては逆なのだけど。
- アセンブラで、レジスタとメモリ?の使い分けって制御している? またはどのように記述すんのか?
※Goもやってみたので追加
package main func main() { rep := 1000000 n := 10000 var x [10000]int for j := 0; j < rep; j++ { // rep 回の繰り返し for i := 0; i < n; i++ { // 一万個の変数それぞれに値を加える x[i]++ } } }
go build main.go
C言語 (最適化前) | C言語 (最適化後) | Java | PHP | Go | |
---|---|---|---|---|---|
real | 0m26.795s | 0m1.484s | 0m1.698s | 9m54.769s | 0m9.053s |
user | 0m26.754s | 0m1.474s | 0m2.134s | 9m53.398s | 0m9.049s |
sys | 0m0.028s | 0m0.006s | 0m0.217s | 0m0.789s | 0m0.008s |