続 C++ はどれくらい速いのか？：バブルソートの演算で比較してみる

# 続 C++ はどれくらい速いのか？：バブルソートの演算で比較してみる

こんにちは、@studio_meowtoon です。今回は、WSL の Ubuntu 環境で C++ プログラムを実行する方法などを紹介します。
![cpp_on_ubuntu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/83f5f450-77ab-71b9-13eb-4633ef1b3a45.png)

## 目的

Windows 11 の Linux でクラウド開発します。

こちらから記事の一覧がご覧いただけます。

https://qiita.com/studio_meowtoon/items/6119ab05d8b13527efd8 

## 実現すること

Ubuntu 22.04 上で、C++ を含む複数のプログラミング言語を使用して、バブルソートの演算の実行速度を比較します。

:::note alert
バブルソートの計算時間だけでは、**言語の性能を評価するのは難しい**ことをあらかじめご承知ください。また各言語での実装内容は、シンプルさを優先していることと、コンパイラや実行環境で**異なる結果**になることを予めご理解ください。
:::

## 比較する言語

|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|

## バブルソートの仕様

以下のような**バブルソートを実行する関数**を各言語で実装します。

```cpp:C++ 版の関数
// バブルソートアルゴリズムを実行する関数
void bubblesort(int array[], int size) {
    for (int i = 0; i < size - 1; ++i) {
        for (int j = 0; j < size - i - 1; ++j) {
            if (array[j] > array[j + 1]) {
                std::swap(array[j], array[j + 1]);
            }
        }
    }
}
```
:::note alert
この記事では、バブルソートアルゴリズムを実行する関数の性能には触れません。ご了承ください。
:::

## 演算時間を取得する手順

### プロジェクトの作成

プロジェクトフォルダを作成します。
※ ~/tmp/bub-comp をプロジェクトフォルダとします。

```sh
$ mkdir -p ~/tmp/bub-comp
$ cd ~/tmp/bub-comp
```

### C++ の処理時間を測定

<details>
<summary>環境構築手順を開きます。</summary>

g++ をインストールします。

```sh
$ sudo apt update
$ sudo apt install g++
```

確認します。

```sh
$  g++ --version
g++ (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0
```

</details>

cpp ファイルを作成します。

```sh
$ vim bubblesort.cpp
```

ファイルの内容

```cpp:bubblesort.cpp ※抜粋
// バブルソートアルゴリズムを実行する関数
void bubblesort(int array[], int size) {
    for (int i = 0; i < size - 1; ++i) {
        for (int j = 0; j < size - i - 1; ++j) {
            if (array[j] > array[j + 1]) {
                swap(array[j], array[j + 1]);
            }
        }
    }
}
```

<details>
<summary>コード全体を表示します。</summary>

```cpp:bubblesort.cpp
#include <chrono>
#include <cstdlib>
#include <ctime>
#include <filesystem>
#include <fstream>
#include <iostream>

using namespace std;
using namespace std::chrono;
using namespace std::filesystem;

const long ARRAY_SIZE = 30000; // ソートする配列のサイズ
const string FILE_PATH = "result/bubblesort_cpp.csv"; // ソート結果を出力するパス

// バブルソートアルゴリズムを実行する関数
void bubblesort(int array[], int size) {
    for (int i = 0; i < size - 1; ++i) {
        for (int j = 0; j < size - i - 1; ++j) {
            if (array[j] > array[j + 1]) {
                swap(array[j], array[j + 1]);
            }
        }
    }
}

// 配列のデータをCSVファイルに出力する関数
void save_array_to_csv(const string& filepath, int array[], int array_size) {
    create_directories(path(filepath).parent_path());
    ofstream outfile(filepath);
    if (outfile.is_open()) {
        outfile << "index,value" << endl;
        for (int i = 0; i < array_size; ++i) {
            outfile << i << "," << array[i] << endl;
        }
        outfile.close();
        cout << "A CSV file was saved successfully." << endl;
    } else {
        cout << "Error: opening CSV file." << endl;
    }
}

int main() {
    // 乱数で配列を初期化
    srand(static_cast<unsigned int>(time(nullptr)));
    int array[ARRAY_SIZE];
    for (int i = 0; i < ARRAY_SIZE; ++i) {
        array[i] = rand() % RAND_MAX;
    }

    // 関数の実行時間を測定
    auto start_time = high_resolution_clock::now();
    bubblesort(array, ARRAY_SIZE);
    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 << "Sorted " << ARRAY_SIZE << " elements in " << fixed << setprecision(5) << setfill('0') << duration_seconds << " seconds." << endl;
    
    // ソート結果をCSVファイルに保存
    save_array_to_csv(FILE_PATH, array, ARRAY_SIZE);

    return 0;
}
```

</details>

<details>
<summary>ビルドして実行する手順を開きます。</summary>

ビルドします。

```sh
$ g++ -o bubblesort_cpp bubblesort.cpp -O2
```

実行します。

```sh
$ ./bubblesort_cpp
```

</details>

出力結果

```sh
Sorted 30000 elements in 0.96081 seconds.
A CSV file was saved successfully.
```

<details>
<summary>実行結果の詳細をみます。</summary>

|回数|時間(秒)|備考|
|:--|:--|:--|
|1|0.96264||
|2|0.95842|MIN|
|3|0.98127|MAX|
|4|0.95899||

:::note warn
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
:::

</details>

### C の処理時間を測定

<details>
<summary>環境構築手順を開きます。</summary>

gcc をインストールします。

```sh
$ sudo apt update
$ sudo apt install gcc
```

確認します。

```sh
$ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
```

</details>

c ファイルを作成します。

```sh
$ vim bubblesort.c
```

ファイルの内容

```cpp:bubblesort.c ※抜粋
// バブルソートアルゴリズムを実行する関数
void bubblesort(int array[], int size) {
    for (int i = 0; i < size - 1; ++i) {
        for (int j = 0; j < size - i - 1; ++j) {
            if (array[j] > array[j + 1]) {
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}
```

<details>
<summary>コード全体を表示します。</summary>

```cpp:bubblesort.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ARRAY_SIZE 30000 // ソートする配列のサイズ
#define FILE_PATH "result/bubblesort_c.csv" // ソート結果を出力するパス

// バブルソートアルゴリズムを実行する関数
void bubblesort(int array[], int size) {
    for (int i = 0; i < size - 1; ++i) {
        for (int j = 0; j < size - i - 1; ++j) {
            if (array[j] > array[j + 1]) {
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}

// 配列のデータをCSVファイルに出力する関数
void save_array_to_csv(const char* filepath, int array[], int array_size) {
    FILE* outfile = fopen(filepath, "w");
    if (outfile != NULL) {
        fprintf(outfile, "index,value\n");
        for (int i = 0; i < array_size; ++i) {
            fprintf(outfile, "%d,%d\n", i, array[i]);
        }
        fclose(outfile);
        printf("A CSV file was saved successfully.\n");
    } else {
        printf("Error: opening CSV file.\n");
    }
}

int main() {
    // 乱数で配列を初期化
    srand((unsigned int)time(NULL));
    int array[ARRAY_SIZE];
    for (int i = 0; i < ARRAY_SIZE; ++i) {
        array[i] = rand() % RAND_MAX;
    }

    // 関数の実行時間を測定
    clock_t start_time = clock();
    bubblesort(array, ARRAY_SIZE);
    clock_t end_time = clock();

    // 実行時間を秒に変換して表示
    double duration_seconds = (double)(end_time - start_time) / CLOCKS_PER_SEC;
    printf("Sorted %d elements in %.5lf seconds.\n", ARRAY_SIZE, duration_seconds);

    // ソート結果をCSVファイルに保存
    save_array_to_csv(FILE_PATH, array, ARRAY_SIZE);

    return 0;
}
```

</details>

<details>
<summary>ビルドして実行する手順を開きます。</summary>

ビルドします。

```sh
$ gcc -o bubblesort_c bubblesort.c -O2
```

実行します。

```sh
$ ./bubblesort_c
```

</details>

出力結果

```sh
Sorted 30000 elements in 0.94438 seconds.
A CSV file was saved successfully.
```

<details>
<summary>実行結果の詳細をみます。</summary>

|回数|時間(秒)|備考|
|:--|:--|:--|
|1|0.94655||
|2|0.94221||
|3|0.94856|MAX|
|4|0.93655|MIN|

:::note warn
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。
:::

</details>

### Java の処理時間を測定

<details>
<summary>環境構築手順を開きます。</summary>

jdk をインストールします。

```
$ sudo apt update
$ sudo apt install openjdk-17-jdk
```

確認します。

```sh
$ 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)
```

</details>

java ファイルを作成します。

```sh
$ vim Bubblesort.java
```

ファイルの内容

```java:Bubblesort.java ※抜粋
// バブルソートアルゴリズムを実行する関数
public static void bubblesort(int[] array) {
    int size = array.length;
    for (int i = 0; i < size - 1; ++i) {
        for (int j = 0; j < size - i - 1; ++j) {
            if (array[j] > array[j + 1]) {
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}
```

<details>
<summary>コード全体を表示します。</summary>

```java:Bubblesort.java
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
import static java.lang.System.*;

public class Bubblesort {
    static final int ARRAY_SIZE = 30000; // ソートする配列のサイズ
    static final String FILE_PATH = "result/bubblesort_java.csv"; // ソート結果を出力するパス

    // バブルソートアルゴリズムを実行する関数
    public static void bubblesort(int[] array) {
        int size = array.length;
        for (int i = 0; i < size - 1; ++i) {
            for (int j = 0; j < size - i - 1; ++j) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }

    // 配列のデータをCSVファイルに出力する関数
    public static void saveArrayToCSV(String filepath, int[] array) {
        try {
            FileWriter writer = new FileWriter(filepath);
            writer.write("index,value\n");
            for (int i = 0; i < array.length; ++i) {
                writer.write(i + "," + array[i] + "\n");
            }
            writer.close();
            out.println("A CSV file was saved successfully.");
        } catch (IOException e) {
            out.println("Error: opening CSV file.");
        }
    }

    public static void main(String[] args) {
        // 乱数で配列を初期化
        int[] array = new int[ARRAY_SIZE];
        Random rand = new Random();
        for (int i = 0; i < ARRAY_SIZE; ++i) {
            array[i] = rand.nextInt(Integer.MAX_VALUE);
        }

        // 関数の実行時間を測定
        long start_time = nanoTime();
        bubblesort(array);
        long end_time = nanoTime();

        // 実行時間を秒に変換して表示
        double duration_seconds = (double) (end_time - start_time) / 1e9;
        out.printf("Sorted %d elements in %.5f seconds.%n", ARRAY_SIZE, duration_seconds);

        // ソート結果をCSVファイルに保存
        saveArrayToCSV(FILE_PATH, array);
    }
}
```

</details>

<details>
<summary>ビルドして実行する手順を開きます。</summary>

ビルドします。

```sh
$ javac Bubblesort.java
```

実行します。

```sh
$ java Bubblesort
```

</details>

出力結果

```sh
Sorted 30000 elements in 1.09717 seconds.
A CSV file was saved successfully.
```

<details>
<summary>実行結果の詳細をみます。</summary>

|回数|時間(秒)|備考|
|:--|:--|:--|
|1|1.09765||
|2|1.10883|MAX|
|3|1.09669||
|4|1.08630|MIN|

:::note warn
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
:::

</details>

### Java (GraalVM ネイティブイメージ) の処理時間を測定

環境構築については以下の記事でご確認いただけます。

https://qiita.com/studio_meowtoon/items/4d11e94a2389758759cd#graalvm-%E3%83%93%E3%83%AB%E3%83%89%E7%92%B0%E5%A2%83

<details>
<summary>ビルドして実行する手順を開きます。</summary>

ネイティブイメージビルドします。

```sh
$ native-image -o Bubblesort_java Bubblesort -O2
```

実行します。

```sh
$ ./Bubblesort_java
```

</details>

出力結果

```sh
Sorted 30000 elements in 0.95360 seconds.
A CSV file was saved successfully.
```

<details>
<summary>実行結果の詳細をみます。</summary>

|回数|時間(秒)|備考|
|:--|:--|:--|
|1|0.95887||
|2|0.94834||
|3|1.03931|MAX|
|4|0.94098|MIN|

:::note warn
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
:::

</details>

### C#.NET の処理時間を測定

<details>
<summary>環境構築手順を開きます。</summary>

dotnet-sdk をインストールします。

```sh
$ sudo apt update
$ sudo apt install dotnet-sdk-7.0
```

確認します。

```sh
$ dotnet --list-sdks
7.0.202 [/usr/share/dotnet/sdk]
$ dotnet --version
7.0.202
```

</details>

cs ファイルを作成します。

```
$ vim Bubblesort.cs
```

ファイルの内容

```csharp:Bubblesort.cs ※抜粋
// バブルソートアルゴリズムを実行する関数
static void bubblesort(int[] array) {
    int size = array.Length;
    for (int i = 0; i < size - 1; ++i) {
        for (int j = 0; j < size - i - 1; ++j) {
            if (array[j] > array[j + 1]) {
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}
```

<details>
<summary>コード全体を表示します。</summary>

```csharp:Bubblesort.cs
using System;
using System.IO;
using static System.Console;
using static System.DateTime;

public class Program {
    const int ARRAY_SIZE = 30000; // ソートする配列のサイズ
    const string FILE_PATH = "result/bubblesort_cs.csv"; // ソート結果を出力するパス

    // バブルソートアルゴリズムを実行する関数
    static void bubblesort(int[] array) {
        int size = array.Length;
        for (int i = 0; i < size - 1; ++i) {
            for (int j = 0; j < size - i - 1; ++j) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }

    // 配列のデータをCSVファイルに出力する関数
    static void saveArrayToCSV(string filepath, int[] array) {
        try {
            using (StreamWriter writer = new(filepath)) {
                writer.WriteLine("index,value");
                for (int i = 0; i < array.Length; ++i) {
                    writer.WriteLine($"{i},{array[i]}");
                }
            }
            WriteLine("A CSV file was saved successfully.");
        } catch (IOException) {
            WriteLine("Error: opening CSV file.");
        }
    }

    static void Main(string[] args) {
        // 乱数で配列を初期化
        int[] array = new int[ARRAY_SIZE];
        Random rand = new();
        for (int i = 0; i < ARRAY_SIZE; ++i) {
            array[i] = rand.Next(int.MaxValue);
        }

        // 関数の実行時間を測定
        long start_time = Now.Ticks;
        bubblesort(array);
        long end_time = Now.Ticks;

        // 実行時間を秒に変換して表示
        double duration_seconds = (end_time - start_time) / 1e7;
        WriteLine($"Sorted {ARRAY_SIZE} elements in {duration_seconds:f5} seconds.");

        // ソート結果をCSVファイルに保存
        saveArrayToCSV(FILE_PATH, array);
    }
}
```

</details>

<details>
<summary>ビルドして実行する手順を開きます。</summary>

Bubblesort.csproj ファイルを作成します。

```sh
$ vim Bubblesort.csproj
```

ファイルの内容

```xml:Bubblesort.csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
  </PropertyGroup>
</Project>
```

ビルドします。

```sh
$ dotnet build Bubblesort.csproj -c Release -o out
```

実行します。

```sh
$ dotnet out/Bubblesort.dll
```

</details>

出力結果

```sh
Sorted 30000 elements in 1.15536 seconds.
A CSV file was saved successfully.
```

<details>
<summary>実行結果の詳細をみます。</summary>

|回数|時間(秒)|備考|
|:--|:--|:--|
|1|1.16973|MAX|
|2|1.15081||
|3|1.15992||
|4|1.14980|MIN|

:::note warn
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
:::

</details>

### Python の処理時間を測定

<details>
<summary>環境構築手順を開きます。</summary>

:::note info
通常 Ubuntu 22.04 には、Python はプリインストールされています。
:::

確認します。

```sh
$ python3 --version
Python 3.10.6
```

</details>

py ファイルを作成します。

```sh
$ vim bubblesort.py
```

ファイルの内容 ※抜粋

```py:bubblesort.py
# バブルソートアルゴリズムを実行する関数
def bubblesort(array):
    size = len(array)
    for i in range(size - 1):
        for j in range(size - i - 1):
            if array[j] > array[j + 1]:
                array[j], array[j + 1] = array[j + 1], array[j]
```

<details>
<summary>コード全体を表示します。</summary>

```py:bubblesort.py
import csv
import random
import time

ARRAY_SIZE = 30000  # ソートする配列のサイズ
FILE_PATH = "result/bubblesort_py.csv"  # ソート結果を出力するパス

# バブルソートアルゴリズムを実行する関数
def bubblesort(array):
    size = len(array)
    for i in range(size - 1):
        for j in range(size - i - 1):
            if array[j] > array[j + 1]:
                array[j], array[j + 1] = array[j + 1], array[j]

# 配列のデータをCSVファイルに出力する関数
def save_array_to_csv(filepath, array):
    try:
        with open(filepath, mode='w', newline='') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow(['index', 'value'])
            for i in range(len(array)):
                writer.writerow([i, array[i]])
        print("A CSV file was saved successfully.")
    except IOError:
        print("Error: opening CSV file.")

def main():
    # 乱数で配列を初期化
    array = []
    for _ in range(ARRAY_SIZE):
        random_value = random.randint(0, 0x7FFFFFFF)
        array.append(random_value)

    # 関数の実行時間を測定
    start_time = time.time()
    bubblesort(array)
    end_time = time.time()

    # 実行時間を秒に変換して表示
    duration_seconds = end_time - start_time
    print(f"Sorted {ARRAY_SIZE} elements in {duration_seconds:.5f} seconds.")

    # ソート結果をCSVファイルに保存
    save_array_to_csv(FILE_PATH, array)

if __name__ == "__main__":
    main()
```

</details>

<details>
<summary>実行する手順を開きます。</summary>

実行します。

```sh
$ python3 bubblesort.py
```

</details>

出力結果

```sh
Sorted 30000 elements in 43.00358 seconds.
A CSV file was saved successfully.
```

<details>
<summary>実行結果の詳細をみます。</summary>

|回数|時間(秒)|備考|
|:--|:--|:--|
|1|42.96953|MIN|
|2|42.97733||
|3|43.02983||
|4|43.16352|MAX|

:::note warn
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
:::

</details>

### Node.js の処理時間を測定

<details>
<summary>環境構築手順を開きます。</summary>

Node.js の公式パッケージリポジトリを追加します。

```sh
$ curl -fsSL https://deb.nodesource.com/setup_19.x | sudo -E bash -
```

Node.js をインストールします。

```sh
$ sudo apt update
$ sudo apt install nodejs
```

確認します。

```sh
$ node -v
v19.8.1
$ npm -v
9.5.1
```

</details>

js ファイルを作成します。

```sh
$ vim bubblesort.js
```

ファイルの内容

```js:bubblesort.js ※抜粋
// バブルソートアルゴリズムを実行する関数
function bubblesort(array) {
    const size = array.length;
    for (let i = 0; i < size - 1; ++i) {
        for (let j = 0; j < size - i - 1; ++j) {
            if (array[j] > array[j + 1]) {
                const temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}
```

<details>
<summary>コード全体を表示します。</summary>

```js:bubblesort.js
const fs = require('fs');

const ARRAY_SIZE = 30000; // ソートする配列のサイズ
const FILE_PATH = 'result/bubblesort_js.csv'; // ソート結果を出力するパス

// バブルソートアルゴリズムを実行する関数
function bubblesort(array) {
    const size = array.length;
    for (let i = 0; i < size - 1; ++i) {
        for (let j = 0; j < size - i - 1; ++j) {
            if (array[j] > array[j + 1]) {
                const temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}

// 配列のデータをCSVファイルに出力する関数
function save_array_to_csv(filepath, array) {
    try {
        const writer = fs.createWriteStream(filepath);
        writer.write('index,value\n');
        for (let i = 0; i < array.length; ++i) {
            writer.write(`${i},${array[i]}\n`);
        }
        writer.end();
        console.log('A CSV file was saved successfully.');
    } catch (error) {
        console.error('Error: opening CSV file.');
    }
}

function main() {
    // 乱数で配列を初期化
    const array = new Array(ARRAY_SIZE);
    for (let i = 0; i < ARRAY_SIZE; ++i) {
        array[i] = Math.floor(Math.random() * 0x7FFFFFFF);
    }

    // 関数の実行時間を測定
    const start_time = process.hrtime.bigint();
    bubblesort(array);
    const end_time = process.hrtime.bigint();

    // 実行時間を秒に変換して表示
    const duration_seconds = Number(end_time - start_time) / 1e9;
    console.log(`Sorted ${ARRAY_SIZE} elements in ${duration_seconds.toFixed(5)} seconds.`);

    // ソート結果をCSVファイルに保存
    save_array_to_csv(FILE_PATH, array);
}

main();
```

</details>

<details>
<summary>実行する手順を開きます。</summary>

実行します。

```sh
$ node bubblesort.js
```

</details>

出力結果

```sh
Sorted 30000 elements in 1.39350 seconds.
A CSV file was saved successfully.
```

<details>
<summary>実行結果の詳細をみます。</summary>

|回数|時間(秒)|備考|
|:--|:--|:--|
|1|1.40223|MAX|
|2|1.38343|MIN|
|3|1.39101||
|4|1.39599||

:::note warn
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
:::

</details>

### Go の処理時間を測定

<details>
<summary>環境構築手順を開きます。</summary>

Go をインストールします。

```sh
$ sudo apt update
$ sudo apt install golang
```

確認します。

```sh
$ go version
go version go1.18.1 linux/amd64
```

</details>

go ファイルを作成します。

```sh
$ bubblesort.go
```

ファイルの内容

```go:bubblesort.go ※抜粋
// バブルソートアルゴリズムを実行する関数
func bubblesort(array []int) {
	size := len(array)
	for i := 0; i < size-1; i++ {
		for j := 0; j < size-i-1; j++ {
			if array[j] > array[j+1] {
				array[j], array[j+1] = array[j+1], array[j]
			}
		}
	}
}
```

<details>
<summary>コード全体を表示します。</summary>

```go:bubblesort.go
package main

import (
	"fmt"
	"math"
	"math/rand"
	"os"
	"time"
)

const ARRAY_SIZE = 30000                     // ソートする配列のサイズ
const FILE_PATH = "result/bubblesort_go.csv" // ソート結果を出力するパス

// バブルソートアルゴリズムを実行する関数
func bubblesort(array []int) {
	size := len(array)
	for i := 0; i < size-1; i++ {
		for j := 0; j < size-i-1; j++ {
			if array[j] > array[j+1] {
				array[j], array[j+1] = array[j+1], array[j]
			}
		}
	}
}

// 配列のデータをCSVファイルに出力する関数
func save_array_to_csv(filepath string, array []int) {
	file, err := os.Create(filepath)
	if err != nil {
		fmt.Println("Error: opening CSV file.")
		return
	}
	defer file.Close()
	file.WriteString("index,value\n")
	for i, value := range array {
		file.WriteString(fmt.Sprintf("%d,%d\n", i, value))
	}
	fmt.Println("A CSV file was saved successfully.")
}

func main() {
	// 乱数で配列を初期化
	rand.Seed(time.Now().UnixNano())
	array := make([]int, ARRAY_SIZE)
	for i := 0; i < ARRAY_SIZE; i++ {
		array[i] = rand.Intn(math.MaxInt32)
	}

	// 関数の実行時間を測定
	start_time := time.Now()
	bubblesort(array)
	end_time := time.Now()

	// 実行時間を秒に変換して表示
	duration_seconds := end_time.Sub(start_time).Seconds()
	fmt.Printf("Sorted %d elements in %.5f seconds.\n", ARRAY_SIZE, duration_seconds)

	// ソート結果をCSVファイルに保存
	save_array_to_csv(FILE_PATH, array)
}
```

</details>

<details>
<summary>ビルドして実行する手順を開きます。</summary>

ビルドします。

```sh
$ go build -o bubblesort_go bubblesort.go
```

実行します。

```sh
$ ./bubblesort_go
```

</details>

出力結果

```sh
Sorted 30000 elements in 0.93943 seconds.
A CSV file was saved successfully.
```

<details>
<summary>実行結果の詳細をみます。</summary>

|回数|時間(秒)|備考|
|:--|:--|:--|
|1|0.93528|MIN|
|2|0.93876||
|3|0.94010||
|4|0.94512|MAX|

:::note warn
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
:::

</details>

### Rust の処理時間を測定

<details>
<summary>環境構築手順を開きます。</summary>

Rust をインストールします。

```sh
$ 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 ファイルに追記します。

```sh
$ vim ~/.bashrc
```

一番下へ次の内容を追加します。

```
# Rust Environment
source "$HOME/.cargo/env"
```

設定を反映します。

```sh
$ source ~/.bashrc
```

確認します。

```sh
$ rustc --version
rustc 1.71.1 (eb26296b5 2023-08-03)
```

</details>

rs ファイルを作成します。

```sh
$ vim bubblesort.rs
```

ファイルの内容

```rust:bubblesort.rs ※抜粋
// バブルソートアルゴリズムを実行する関数
fn bubblesort(array: &mut [i32]) {
    let size = array.len();
    for i in 0..size - 1 {
        for j in 0..size - i - 1 {
            if array[j] > array[j + 1] {
                array.swap(j, j + 1);
            }
        }
    }
}
```

<details>
<summary>コード全体を表示します。</summary>

```rust:bubblesort.rs
use std::fs::File;
use std::io::Write;
use std::time::Instant;
use rand::Rng;

const ARRAY_SIZE: usize = 30000; // ソートする配列のサイズ
const FILE_PATH: &str = "result/bubblesort_rs.csv"; // ソート結果を出力するパス

// バブルソートアルゴリズムを実行する関数
fn bubblesort(array: &mut [i32]) {
    let size = array.len();
    for i in 0..size - 1 {
        for j in 0..size - i - 1 {
            if array[j] > array[j + 1] {
                array.swap(j, j + 1);
            }
        }
    }
}

// 配列のデータをCSVファイルに出力する関数
fn save_array_to_csv(filepath: &str, array: &[i32]) {
    let mut file = File::create(filepath).expect("Error: creating CSV file.");
    file.write_all(b"index,value\n").expect("Error: writing CSV header.");
    for (i, &value) in array.iter().enumerate() {
        writeln!(file, "{},{}", i, value).expect("Error: writing CSV data.");
    }
    println!("A CSV file was saved successfully.");
}

fn main() {
    // 乱数で配列を初期化
    let mut array = Vec::with_capacity(ARRAY_SIZE);
    let mut rng = rand::thread_rng();
    for _ in 0..ARRAY_SIZE {
        array.push(rng.gen_range(0..i32::MAX));
    }

    // 関数の実行時間を測定
    let start_time = Instant::now();
    bubblesort(&mut array);
    let end_time = Instant::now();

    // 実行時間を秒に変換して表示
    let duration_seconds = end_time.duration_since(start_time).as_secs_f64();
    println!("Sorted {} elements in {:.5} seconds.", ARRAY_SIZE, duration_seconds);

    // ソート結果をCSVファイルに保存
    save_array_to_csv(FILE_PATH, &array);
}
```

</details>

<details>
<summary>ビルドして実行する手順を開きます。</summary>

Cargo.toml ファイルを作成します。

```sh
$ vim Cargo.toml
```

ファイルの内容

```sh:Cargo.toml
[package]
name = "bubblesort_rs"
version = "0.1.0"
edition = "2021"

[dependencies]
rand = "0.8"

[[bin]]
name = "bubblesort"
path = "bubblesort.rs"
```

ビルドします。

```sh
$ cargo build --release --bin bubblesort
```

実行します。

```sh
$ ./target/release/bubblesort_rs
```

</details>

出力結果

```sh
Sorted 30000 elements in 1.00214 seconds.
A CSV file was saved successfully.
```

<details>
<summary>実行結果の詳細をみます。</summary>

|回数|時間(秒)|備考|
|:--|:--|:--|
|1|1.00304||
|2|1.00125||
|3|1.00098|MIN|
|4|1.01357|MAX|

:::note warn
4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
:::

</details>

### シェルスクリプト の処理時間を測定

sh ファイルを作成します。

```sh
$ vim bubblesort.sh
```

ファイルの内容

```sh:bubblesort.sh ※抜粋
# バブルソートアルゴリズムを実行する関数
bubblesort() {
    local array=("$@")
    local size=${#array[@]}
    for ((i = 0; i < size - 1; i++)); do
        for ((j = 0; j < size - i - 1; j++)); do
            if ((array[j] > array[j + 1])); then
                temp=${array[j]}
                array[j]=${array[j + 1]}
                array[j + 1]=$temp
            fi
        done
    done
    echo "${array[@]}"
}
```

<details>
<summary>コード全体を表示します。</summary>

```sh:bubblesort.sh
#!/bin/bash

ARRAY_SIZE=30000 # ソートする配列のサイズ
FILE_PATH="result/bubblesort_sh.csv" # ソート結果を出力するパス

# バブルソートアルゴリズムを実行する関数
bubblesort() {
    local array=("$@")
    local size=${#array[@]}
    for ((i = 0; i < size - 1; i++)); do
        for ((j = 0; j < size - i - 1; j++)); do
            if ((array[j] > array[j + 1])); then
                temp=${array[j]}
                array[j]=${array[j + 1]}
                array[j + 1]=$temp
            fi
        done
    done
    echo "${array[@]}"
}

# 配列のデータをCSVファイルに出力する関数
save_array_to_csv() {
    local filepath=$1
    shift
    local array=("$@")
    echo "index,value" > "$filepath"
    for ((i = 0; i < ${#array[@]}; i++)); do
        echo "$i,${array[i]}" >> "$filepath"
    done
    echo "A CSV file was saved successfully."
}

# 乱数で配列を初期化
array=()
for ((i = 0; i < ARRAY_SIZE; i++)); do
    array[i]=$((RANDOM % 0x7FFFFFFF))
done

# 関数の実行時間を測定
start_time=$(date +%s.%N)
sorted_array=($(bubblesort "${array[@]}"))
end_time=$(date +%s.%N)

# 実行時間を計算して表示
duration_seconds=$(echo "$end_time - $start_time" | bc)
duration_milliseconds=$(printf "%.5f" "$duration_seconds")
echo "Sorted $ARRAY_SIZE elements in $duration_milliseconds seconds."

# ソート結果をCSVファイルに保存
save_array_to_csv "$FILE_PATH" "${sorted_array[@]}"
```

</details>

<details>
<summary>実行する手順を開きます。</summary>

実行します。

```sh
$ chmod +x bubblesort.sh
$ ./bubblesort.sh
```

</details>

出力結果

```sh
※ 計測断念: 遅すぎる😭
```

:::note warn
シェルスクリプトでは、一般的なプログラム言語と同じような書き方をすると効率的でない場合があります。
:::

## 計測した結果を比較

**実行環境**

|項目|内容|
|:--|:--|
|プロセッサ|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|Go (go1.18.1 linux/amd64)       |ネイティブ   |0.93943|0.856|
|2|C (gcc 11.4.0)                  |ネイティブ   |0.94438|0.861|
|3|Java (GraalVM 22.3.1 Java 17 CE)|ネイティブ   |0.95360|0.869|
|4|C++ (g++ 11.3.0)                |ネイティブ   |0.96081|0.876|
|5|Rust (rustc 1.71.1)             |ネイティブ   |1.00214|0.913|
|6|Java (openjdk version "17.0.7") |JITコンパイル|1.09717|1.000|
|7|C#.NET (dotnet 7.0.202)         |JITコンパイル|1.15536|1.054|
|8|Node.js (node v19.8.1)          |インタプリタ |1.39350|1.270|
|9|Python (python 3.10.6)          |インタプリタ |43.00358|39.175|

:::note warn
各言語で4回の実行結果から最大値と最小値を取り除いて、残りの2回の結果の平均値を求めています。※小数点5桁以下は切り捨て
:::

**結果からわかったこと**

|キーワード|考察|
|:--|:--|
|ネイティブコンパイルの優位性|C や C++、Go、Rust などのネイティブコンパイル言語が、JIT コンパイルやインタプリタを使用する言語に比べて処理速度が速いことが示されています。|
|インタプリタの遅さ|インタプリタを使用する Python や Node.js では処理時間が比較的長くなっており、実行速度が他の言語より遅いことが示されています。|

:::note info
Java では、GraalVM ネイティブイメージビルドにより高速化を期待しました。結果、**バブルソートの演算に関しては**今回の計測結果においては、ネイティブイメージ実行の方が JIT コンパイル最適化よりも若干高速でした。
:::

## まとめ

+ バブルソートの演算に関しては、ネイティブコードを生成する言語がほぼ同じようなパフォーマンスで動作しました。
+ Java、C# の JITコンパイル方式の言語においても、バブルソートの演算に関してはかなり優秀な性能を示しています。
+ また、各言語でのファイル出力の方法について学ぶことができました。

:::note warn
本記事は、Ubuntu 22.04 上でのプログラム実行速度比較に焦点を当て、バブルソート演算を通じて異なるプログラミング言語のパフォーマンスを調査しました。クラウド環境におけるアプリケーション開発者にとって、言語選定の際の参考となれば幸いです。
:::

どうでしたか？ Windows 11 の Linux で、クラウド向けの開発環境を手軽に構築することができます。ぜひお試しください。今後もさまざまな言語や開発環境の情報などを紹介していきますので、ぜひお楽しみにしてください。

## 関連記事

https://qiita.com/studio_meowtoon/items/74764a835be31c1e73c6