C++とJavaとSwiftの速度比較 〜配列編〜

SwiftはAppleが2014年に発表した非常に新しいプログラミング言語です。 コンパイルすることで高速な実行が可能でありながら、スクリプト言語のようなモダンな文法を多く取り入れており、さらにMacだけでなくLinuxでもコンパイル可能。「C++やJavaから乗り換えようかな?」と考えた人も少なくないはず。

ただ、乗り換えると言っても「C++やJavaと比べてパフォーマンスはどうなの?」という疑問もあります。

ということで、今回はC++やJavaとSwift(ついでにPython)の、配列アクセス観点からの速度比較を行ってみました。

(2017.08.22 追記)
より複雑な処理でも比較してみました

実験環境

以下、今回の実験環境です。全て同じハードウェア上で動かしています(ハードウェアの情報は割愛します)。

OS

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.3 LTS"

Swift

$ swift --version
Swift version 3.1.1 (swift-3.1.1-RELEASE)
Target: x86_64-unknown-linux-gnu

コンパイルオプション

$ swiftc -O

C++

$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
(中略)
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) 

コンパイルオプション

$ g++ -O2 -std=c++11

Java

$ java -version
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)

Python

$ python --version
Python 3.5.2

ソースコード

今回は配列アクセスの速度比較ということで、配列の要素に順次アクセスし、代入・参照を行うプログラムを用いました。

  • 2の26乗の長さの整数型の配列を確保し、全要素を0で初期化。
  • 配列の各要素にシーケンシャルにアクセスし、数値を代入。
  • 配列の各要素にシーケンシャルにアクセスし、総和を求める。

という処理を10回繰り返すプログラムです。
(※Pythonは非常に実行が遅いため処理を10回繰り返すということはしていません。結果では値を10倍して記載しています。)

Swift

SwiftのArrayはアクセスの際に領域外判定を行うため、バグにすぐ気づくことができる一方で処理は多少遅くなると考えられます。

Swiftにも領域の判定を行わない、C++のポインタのような機能もあるようですが、今回の実験には用いていません。

C++

C++はnewで確保したintの配列を使ったものと、STLのvectorを使ったものそれぞれを比較に用いました。

配列はvectorと違い便利なメソッドも無く領域外アクセスのリスクも高いです。以下のソースを見てもらえばわかりますが、ソースの可読性も下がり、開発コストは増します。ただその分動作は早いと思われます。

Java

Javaでは配列(int)とArrayList(Integer)のそれぞれを用いています。 ArrayListはSwiftのArray同様に、様々なメソッドを持っており範囲外へのアクセスもチェックしています。

JVM上で動く分C++やSwiftに比べたら遅くなるのではないかと思います。

Python

スクリプト言語のため動作は非常に遅いですが、コードはとてもシンプルに書けます。速度にシビアでなければPythonで十分という場面は多くあります。

速度面では他のコンパイル言語に勝てるわけは無いのですが、一応参考として比較してみました。
(※Pythonは非常に実行が遅いため処理を10回繰り返すということはしていません。結果では値を10倍して記載しています。)

結果

時間の計測にはtimeコマンドを用い、5回実行したときのそれぞれのユーザーCPU時間の平均を実行時間としました。

言語 実行時間(秒) C++(配列)との差
C++(配列) 1.5008 -
C++(vector) 1.5152 0.96%
Swift 1.8296 21.91%
Java(配列) 1.8072 20.42%
Java(ArrayList) 6.9064 360.18%
Python 83.824 5485.29%

それぞれの実行時間をグラフにしました。

f:id:albow:20170817190842p:plain

Pythonがあまりに遅すぎてよくわからないので、Pythonを抜いたグラフも載せておきます。

f:id:albow:20170817190858p:plain

順位自体はほぼ想定通りでしょうか。

C++は配列でもvectorでもほぼ速度が変わりませんでした。コードによってコンパイラの最適化の効き具合が変わってくる可能性もありますが、この結果を信じるならばほとんどの場合においてvectorを使った方が良さそうです。

Swiftは領域外アクセスのチェックをしているにも関わらずJavaの配列とほぼ変わらないという結果に。もちろんC++ほど速くはないのですが、その差は約20%。これをどう捉えるかは用途によりそうです。

JavaはArrayListを使った場合の速度低下が著しいですね。著しすぎませんかね?試しにintの配列をIntegerの配列に変えたところかなり速度低下したため、これは配列とArrayListというよりintとIntegerの差がかなり効いていそうです。Swiftとの比較が微妙になってしまいましたね。。。

おわりに

C++とJavaとSwiftの違いを見てみましたが、いかがでしたでしょうか。 思ったより違う or 思ったより違わない など、いろいろ思うところはあるかと思います。

SwiftがC++やJavaの代わりになるか?という問いに対しても、場合によりけりとしか言いようがないですね。

今回の比較自体が言語の性能のほんの一部を比べただけですし、この結果だけで言語の優劣を論じることはできません。 何より言語は用途によって使い分けるべきものですので、その判断のためのよい材料になればと思います。

この書き方の方が速いよ!とか、この比べ方はまずいよ!など、なにか指摘があればコメントいただけると幸いです。

(2017.08.22 追記)
より複雑な処理でも比較してみました:C++とSwiftの速度比較 〜ソートアルゴリズム編〜


既にC言語を知っている人で、C++を追加で学習したいという方におすすめ。

独習C++ 第4版

独習C++ 第4版


C++は勉強したことあるけどC++11の文法(今回のソースコードで使用しているautoや範囲ベースforなど)は知らない、という方におすすめ。Swiftと比較するのであれば、今のC++は何ができるのかをきちんと知っておくべきでしょう。

Effective Modern C++ ―C++11/14プログラムを進化させる42項目

Effective Modern C++ ―C++11/14プログラムを進化させる42項目