C++ はどれくらい速いのか?:フィボナッチ数列の演算で比較してみる
こんにちは、@studio_meowtoon です。今回は、WSL の Ubuntu 環境で C++ プログラムを実行する方法などを紹介します。
目的
Windows 11 の Linux でクラウド開発します。
こちらから記事の一覧がご覧いただけます。
実現すること
Ubuntu 22.04 上で、C++ を含む複数のプログラミング言語を使用して、フィボナッチ数列を求める関数の実行速度を比較します。
フィボナッチ数列の計算時間では、言語の性能を評価するのは難しいことをあらかじめご承知ください。また各言語での実装内容は、シンプルさを優先していることと、コンパイラや実行環境で異なる結果になることを予めご理解ください。
比較する言語
No | 言語/環境 | 実行形式 | コンパイラ/実行環境 |
---|---|---|---|
1 | C++ | ネイティブ | g++ 11.3.0 |
2 | C | ネイティブ | gcc 11.4.0 |
3 | Java | JITコンパイル | openjdk version "17.0.7" |
4 | Java | ネイティブ | GraalVM 22.3.1 Java 17 CE |
5 | C#.NET | JITコンパイル | dotnet 7.0.202 |
6 | Python | インタプリタ | python 3.10.6 |
7 | Node.js | インタプリタ | node v19.8.1 |
8 | Go | ネイティブ | go1.18.1 linux/amd64 |
9 | Rust | ネイティブ | rustc 1.71.1 |
おまけ | シェルスクリプト | インタプリタ | GNU bash version 5.1.16(1)-release |
フィボナッチ数を求める仕様
以下のような再帰的処理を各言語で実装します。
// フィボナッチ数列を再帰的な方法で計算する関数
int fibonacci(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
この記事では、フィボナッチ数列を再帰的な方法で計算する関数の性能には触れません。ご了承ください。
演算時間を取得する手順
プロジェクトの作成
プロジェクトフォルダを作成します。
※ ~/tmp/fib-comp をプロジェクトフォルダとします。
$ mkdir -p ~/tmp/fib-comp
$ cd ~/tmp/fib-comp
C++ の処理時間を測定
環境構築手順を開きます。
g++ をインストールします。
$ sudo apt update
$ sudo apt install g++
確認します。
$ g++ --version
g++ (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0
cpp ファイルを作成します。
$ vim fibonacci.cpp
ファイルの内容
// フィボナッチ数列を再帰的な方法で計算する関数
int fibonacci(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
コード全体を表示します。
#include <chrono>
#include <iomanip>
#include <iostream>
using namespace std;
using namespace std::chrono;
const int TERM = 44; // フィボナッチ数列の項数を定義
// フィボナッチ数列を再帰的な方法で計算する関数
int fibonacci(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
int main() {
// 関数の実行時間を測定
auto start_time = high_resolution_clock::now();
int result = fibonacci(TERM);
auto end_time = high_resolution_clock::now();
// 実行時間を秒に変換して表示
auto duration_microseconds = duration_cast<microseconds>(end_time - start_time).count();
double duration_seconds = static_cast<double>(duration_microseconds) / 1e6;
cout << "Result: " << result << endl;
cout << "Time: " << fixed << setprecision(5) << setfill('0') << duration_seconds << " seconds" << endl;
return 0;
}
ビルドして実行する手順を開きます。
ビルドします。
$ g++ -o fibonacci_cpp fibonacci.cpp -O2
実行します。
$ ./fibonacci_cpp
出力結果
Result: 701408733
Time: 0.92987 seconds
実行結果の詳細をみます。
回数 | 時間(秒) | 備考 |
---|---|---|
1 | 0.92823 | |
2 | 0.93151 | |
3 | 0.94174 | MAX |
4 | 0.92752 | MIN |
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
C の処理時間を測定
環境構築手順を開きます。
gcc をインストールします。
$ sudo apt update
$ sudo apt install gcc
確認します。
$ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
c ファイルを作成します。
$ vim fibonacci.c
ファイルの内容
// フィボナッチ数列を再帰的な方法で計算する関数
int fibonacci(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
コード全体を表示します。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
const int TERM = 44; // フィボナッチ数列の項数を定義
// フィボナッチ数列を再帰的な方法で計算する関数
int fibonacci(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
int main() {
// 関数の実行時間を測定
clock_t start_time = clock();
int result = fibonacci(TERM);
clock_t end_time = clock();
// 実行時間を秒に変換して表示
double duration_seconds = (double)(end_time - start_time) / 1e6;
printf("Result: %d\n", result);
printf("Time: %.5lf seconds\n", duration_seconds);
return 0;
}
ビルドして実行する手順を開きます。
ビルドします。
$ gcc -o fibonacci_c fibonacci.c -O2
実行します。
$ ./fibonacci_c
出力結果
Result: 701408733
Time: 0.91076 seconds
実行結果の詳細をみます。
回数 | 時間(秒) | 備考 |
---|---|---|
1 | 0.91894 | MAX |
2 | 0.90905 | MIN |
3 | 0.91053 | |
4 | 0.91099 |
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。
Java の処理時間を測定
環境構築手順を開きます。
jdk をインストールします。
$ sudo apt update
$ sudo apt install openjdk-17-jdk
確認します。
$ java -version
openjdk version "17.0.7" 2023-04-18
OpenJDK Runtime Environment (build 17.0.7+7-Ubuntu-0ubuntu122.04.2)
OpenJDK 64-Bit Server VM (build 17.0.7+7-Ubuntu-0ubuntu122.04.2, mixed mode, sharing)
java ファイルを作成します。
$ vim Fibonacci.java
ファイルの内容
// フィボナッチ数列を再帰的な方法で計算する関数
static int fibonacci(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
コード全体を表示します。
import static java.lang.System.*;
public class Fibonacci {
static final int TERM = 44; // フィボナッチ数列の項数を定義
// フィボナッチ数列を再帰的な方法で計算する関数
static int fibonacci(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
public static void main(String[] args) {
// 再帰的な方法での計算時間を測定
long start_time = nanoTime();
int result = fibonacci(TERM);
long end_time = nanoTime();
// 実行時間を秒に変換して表示
double duration_seconds = (end_time - start_time) / 1e9;
out.println("Result: " + result);
out.printf("Time: %.5f seconds%n", duration_seconds);
}
}
ビルドして実行する手順を開きます。
ビルドします。
$ javac Fibonacci.java
実行します。
$ java Fibonacci
出力結果
Result: 701408733
Time: 2.70795 seconds
実行結果の詳細をみます。
回数 | 時間(秒) | 備考 |
---|---|---|
1 | 2.72754 | MAX |
2 | 2.71025 | |
3 | 2.70565 | |
4 | 2.69725 | MIN |
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
Java (GraalVM ネイティブイメージ) の処理時間を測定
環境構築については以下の記事でご確認いただけます。
ビルドして実行する手順を開きます。
ネイティブイメージビルドします。
$ native-image -o Fibonacci_java Fibonacci -O2
実行します。
$ ./Fibonacci_java
出力結果
Result: 701408733
Time: 3.28288 seconds
実行結果の詳細をみます。
回数 | 時間(秒) | 備考 |
---|---|---|
1 | 3.29400 | |
2 | 3.35933 | MAX |
3 | 3.27176 | |
4 | 3.25840 | MIN |
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
C#.NET の処理時間を測定
環境構築手順を開きます。
dotnet-sdk をインストールします。
$ sudo apt update
$ sudo apt install dotnet-sdk-7.0
確認します。
$ dotnet --list-sdks
7.0.202 [/usr/share/dotnet/sdk]
$ dotnet --version
7.0.202
cs ファイルを作成します。
$ vim Fibonacci.cs
ファイルの内容
// フィボナッチ数列を再帰的な方法で計算する関数
static int fibonacci(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
コード全体を表示します。
using static System.Console;
using static System.DateTime;
public class Program {
const int TERM = 44; // フィボナッチ数列の項数を定義
// フィボナッチ数列を再帰的な方法で計算する関数
static int fibonacci(int n) {
if (n == 0 || n == 1) {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
public static void Main(string[] args) {
// 再帰的な方法での計算時間を測定
long start_time = Now.Ticks;
int result = fibonacci(TERM);
long end_time = Now.Ticks;
// 実行時間を秒に変換して表示
double duration_seconds = (end_time - start_time) / 1e7;
WriteLine($"Result: {result}");
WriteLine($"Time: {duration_seconds:f5} seconds");
}
}
ビルドして実行する手順を開きます。
Fibonacci.csproj ファイルを作成します。
$ vim Fibonacci.csproj
ファイルの内容
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
</Project>
ビルドします。
$ dotnet build Fibonacci.csproj -c Release -o out
実行します。
$ dotnet out/Fibonacci.dll
出力結果
Result: 701408733
Time: 2.92658 seconds
実行結果の詳細をみます。
回数 | 時間(秒) | 備考 |
---|---|---|
1 | 2.94822 | MAX |
2 | 2.91312 | MIN |
3 | 2.92032 | |
4 | 2.93285 |
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
Python の処理時間を測定
環境構築手順を開きます。
通常 Ubuntu 22.04 には、Python はプリインストールされています。
確認します。
$ python3 --version
Python 3.10.6
py ファイルを作成します。
$ vim fibonacci.py
ファイルの内容
# フィボナッチ数列を再帰的な方法で計算する関数
def fibonacci(n):
if n == 0 or n == 1:
return n
else:
return fibonacci(n - 2) + fibonacci(n - 1)
コード全体を表示します。
import time
TERM = 44 # フィボナッチ数列の項数を定義
# フィボナッチ数列を再帰的な方法で計算する関数
def fibonacci(n):
if n == 0 or n == 1:
return n
else:
return fibonacci(n - 2) + fibonacci(n - 1)
def main():
# 関数の実行時間を測定
start_time = time.time()
result = fibonacci(TERM)
end_time = time.time()
# 実行時間を秒に変換して表示
duration_seconds = end_time - start_time
print("Result:", result)
print("Time: {:.5f} seconds".format(duration_seconds))
if __name__ == "__main__":
main()
実行する手順を開きます。
実行します。
$ python3 fibonacci.py
出力結果
Result: 701408733
Time: 162.69341 seconds
実行結果の詳細をみます。
回数 | 時間(秒) | 備考 |
---|---|---|
1 | 166.18202 | |
2 | 162.69341 | |
3 | 176.12160 | MAX |
4 | 160.30293 | MIN |
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
Node.js の処理時間を測定
環境構築手順を開きます。
Node.js の公式パッケージリポジトリを追加します。
$ curl -fsSL https://deb.nodesource.com/setup_19.x | sudo -E bash -
Node.js をインストールします。
$ sudo apt update
$ sudo apt install nodejs
確認します。
$ node -v
v19.8.1
$ npm -v
9.5.1
js ファイルを作成します。
$ vim fibonacci.js
ファイルの内容
// フィボナッチ数列を再帰的な方法で計算する関数
function fibonacci(n) {
if (n === 0 || n === 1) {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
コード全体を表示します。
const TERM = 44; // フィボナッチ数列の項数を定義
// フィボナッチ数列を再帰的な方法で計算する関数
function fibonacci(n) {
if (n === 0 || n === 1) {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
function main() {
// 関数の実行時間を測定
const start_time = process.hrtime.bigint();
const result = fibonacci(TERM);
const end_time = process.hrtime.bigint();
// 実行時間をミリ秒に変換して表示
const duration_ms = Number(end_time - start_time) / 1e9;
console.log("Result:", result);
console.log("Time:", duration_ms.toFixed(5), "seconds");
}
main();
実行する手順を開きます。
実行します。
$ node fibonacci.js
出力結果
Result: 701408733
Time: 6.31335 seconds
実行結果の詳細をみます。
回数 | 時間(秒) | 備考 |
---|---|---|
1 | 6.31413 | |
2 | 6.32342 | MAX |
3 | 6.28792 | MIN |
4 | 6.31257 |
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
Go の処理時間を測定
環境構築手順を開きます。
Go をインストールします。
$ sudo apt update
$ sudo apt install golang
確認します。
$ go version
go version go1.18.1 linux/amd64
go ファイルを作成します。
$ fibonacci.go
ファイルの内容
// フィボナッチ数列を再帰的な方法で計算する関数
func fibonacci(n int) int {
if n == 0 || n == 1 {
return n
} else {
return fibonacci(n-2) + fibonacci(n-1)
}
}
コード全体を表示します。
package main
import (
"fmt"
"time"
)
const TERM = 44 // フィボナッチ数列の項数を定義
// フィボナッチ数列を再帰的な方法で計算する関数
func fibonacci(n int) int {
if n == 0 || n == 1 {
return n
} else {
return fibonacci(n-2) + fibonacci(n-1)
}
}
func main() {
// 関数の実行時間を測定
start_time := time.Now()
result := fibonacci(TERM)
end_time := time.Now()
// 実行時間を秒に変換して表示
duration := end_time.Sub(start_time).Seconds()
fmt.Println("Result:", result)
fmt.Println("Time:", fmt.Sprintf("%.5f", duration), "seconds")
}
ビルドして実行する手順を開きます。
ビルドします。
$ go build -o fibonacci_go fibonacci.go
実行します。
$ ./fibonacci_go
出力結果
Result: 701408733
Time: 2.76093 seconds
実行結果の詳細をみます。
回数 | 時間(秒) | 備考 |
---|---|---|
1 | 2.75142 | |
2 | 2.79774 | MAX |
3 | 2.75108 | MIN |
4 | 2.77044 |
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
Rust の処理時間を測定
環境構築手順を開きます。
Rust をインストールします。
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
> 1
Rust の環境設定を .bashrc ファイルに追記します。
$ vim ~/.bashrc
一番下へ次の内容を追加します。
# Rust Environment
source "$HOME/.cargo/env"
設定を反映します。
$ source ~/.bashrc
確認します。
$ rustc --version
rustc 1.71.1 (eb26296b5 2023-08-03)
rs ファイルを作成します。
$ vim fibonacci.rs
ファイルの内容
// フィボナッチ数列を再帰的な方法で計算する関数
fn fibonacci(n: i32) -> i32 {
if n == 0 || n == 1 {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
コード全体を表示します。
use std::time::Instant;
const TERM: i32 = 44; // フィボナッチ数列の項数を定義
// フィボナッチ数列を再帰的な方法で計算する関数
fn fibonacci(n: i32) -> i32 {
if n == 0 || n == 1 {
return n;
} else {
return fibonacci(n - 2) + fibonacci(n - 1);
}
}
fn main() {
// 関数の実行時間を測定
let start_time = Instant::now();
let result = fibonacci(TERM);
let end_time = Instant::now();
// 実行時間を秒に変換して表示
let duration = end_time - start_time;
let duration_seconds = duration.as_secs_f64();
println!("Result: {}", result);
println!("Time: {:.5} seconds", duration_seconds);
}
ビルドして実行する手順を開きます。
ビルドします。
$ rustc -C opt-level=2 -o fibonacci_rs fibonacci.rs
実行します。
$ ./fibonacci_rs
出力結果
Result: 701408733
Time: 1.72496 seconds
実行結果の詳細をみます。
回数 | 時間(秒) | 備考 |
---|---|---|
1 | 1.75236 | |
2 | 1.75924 | MAX |
3 | 1.71937 | MIN |
4 | 1.72496 |
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
シェルスクリプト の処理時間を測定
sh ファイルを作成します。
$ vim fibonacci.sh
ファイルの内容
# フィボナッチ数列を再帰的な方法で計算する関数
fibonacci() {
n=$1
if [ $n -eq 0 ] || [ $n -eq 1 ]; then
echo $n
else
echo $(expr $(fibonacci $(expr $n - 2)) + $(fibonacci $(expr $n - 1)))
fi
}
コード全体を表示します。
#!/bin/sh
TERM=44 # フィボナッチ数列の項数を定義
# フィボナッチ数列を再帰的な方法で計算する関数
fibonacci() {
n=$1
if [ $n -eq 0 ] || [ $n -eq 1 ]; then
echo $n
else
echo $(expr $(fibonacci $(expr $n - 2)) + $(fibonacci $(expr $n - 1)))
fi
}
# 関数の実行時間を測定
start_time=$(date +%s%N)
result=$(fibonacci $TERM)
end_time=$(date +%s%N)
# 実行時間を秒に変換して表示
duration_nanoseconds=$(expr $end_time - $start_time)
duration_seconds=$(awk "BEGIN {print $duration_nanoseconds / 1e9}")
echo "Result: $result"
echo "Time: $duration_seconds seconds"
実行する手順を開きます。
実行します。
$ chmod +x fibonacci.sh
$ ./fibonacci.sh
出力結果
※ 計測断念: 遅すぎる😭
シェルスクリプトでは、一般的なプログラム言語と同じような書き方をすると効率的でない場合があります。
計測した結果を比較
実行環境
項目 | 内容 |
---|---|
プロセッサ | Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz 2.59 GHz |
実装 RAM | 16.0 GB |
システムの種類 | 64 ビット オペレーティング システム、x64 ベース プロセッサ |
OS | Ubuntu 22.04.1 LTS (Windows 11 WSL) |
計測結果
順位 | 言語/コンパイラ/環境 | 実行形式 | 処理時間(秒) | Java JITコンパイルを 1.0 とした割合 |
---|---|---|---|---|
1 | C (gcc 11.4.0) | ネイティブ | 0.91076 | 0.336 |
2 | C++ (g++ 11.3.0) | ネイティブ | 0.92987 | 0.344 |
3 | Rust (rustc 1.71.1) | ネイティブ | 1.72496 | 0.637 |
4 | Java (openjdk version "17.0.7") | JITコンパイル | 2.70795 | 1.000 |
5 | Go (go1.18.1 linux/amd64) | ネイティブ | 2.76093 | 1.018 |
6 | C#.NET (dotnet 7.0.202) | JITコンパイル | 2.92658 | 1.080 |
7 | Java (GraalVM 22.3.1 Java 17 CE) | ネイティブ | 3.28288 | 1.210 |
8 | Node.js (node v19.8.1) | インタプリタ | 6.31335 | 2.324 |
9 | Python (python 3.10.6) | インタプリタ | 162.69341 | 60.063 |
各言語で4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
結果からわかること
キーワード | 考察 |
---|---|
ネイティブコンパイルの優位性 | C や C++、Rust などのネイティブコンパイル言語が、JIT コンパイルやインタプリタを使用する言語に比べて処理速度が速いことが示されています。Go に関しては今回の実装では優位性は見られませんでした。 |
インタプリタの遅さ | インタプリタを使用する Python や Node.js では処理時間が比較的長くなっており、実行速度が他の言語より遅いことが示されています。 |
Java では、GraalVM ネイティブイメージビルドにより高速化が期待されましたが、再帰的なフィボナッチ数演算に関しては今回の計測結果においては、JIT コンパイル最適化の方がネイティブイメージ実行よりも高速でした。
まとめ
- 再帰的なフィボナッチ数演算に関しては、C、C++ はダントツで高速でした😮
- また、各言語でのプログラム実行方法や、時間の処理について学ぶことができました。
本記事は、Ubuntu 22.04 上でのプログラム実行速度比較に焦点を当て、フィボナッチ数列の求め方を通じて異なるプログラミング言語のパフォーマンスを調査しました。クラウド環境におけるアプリケーション開発者にとって、言語選定の際の参考となれば幸いです。
どうでしたか? Windows 11 の Linux で、クラウド向けの開発環境を手軽に構築することができます。ぜひお試しください。今後もさまざまな言語や開発環境の情報などを紹介していきますので、ぜひお楽しみにしてください。
関連コンテンツ