クーの自由研究

マスターの かえるのクーは8年の任期を終え消失しました。クーⅡ世の中の人絶賛募集中です。(特にオリオン座&プレイアデス方向の方は優遇します)助手がメイド喫茶から生還し、リハビリを兼ねて復帰しています。

用もないのにC++とRustの名誉を挽回してみる

速さこそ正義!

かえるのクーの助手の「井戸中 聖」(いとなか セイ)でございます。

  

C++とRustの「連想配列/HashMap」がことのほか遅いことがわかって、一瞬そんなにも速くないイメージを持ってしまいました。

「C++とRUSTが遅い訳ないじゃん。」という天の声が聞こえました。また、大量データで速度を求めるなら『ハッシュ(unordered_map)」ではなく「マップ(map)」を用いるべきである。初心者にも程がある。』という地の声も聞こえました。C++は初心者だと思ってましたが、それですらなく「初めて」作成したことに気が付きました。(言い訳:UNIX C言語なら自信がありました)

なので、通常の動的配列(ベクトル系)で爆速なのを確認するだけの2番煎じ記事です。

 

本当はベクトル系ではなく、array系(固定長配列)で比較しようと思ったのですが、3億弱程度の配列定義でコンパイルが通らなくなり、より実用性の高いvectorでの比較となります。

本題の「何故Javaで用もないのにマルチスレッド処理になるのか?」については調査進捗がございませんので、ケイゾクとさせていただきます。

比較レギュレーションは以下のとおり

・簡単なオブジェクト(クラスもしくは構造体のインスタンス)を4億個準備する。(パラメータ値あり)

オブジェクトをVectorに格納する。(Vectorの個数はコーディング定義時に指定してよい)

・上記のデータの準備にどれくらいかかるか測定する。

・キャッシュの影響をなくすため、そのまま30秒待つ。

4億個のインスタンスを順次取得してパラメータ値を加算する。

・加算した値を表示する。

・加算にかかる時間を計測する。

 

結果:

ご覧のとおり、RustとC++は爆速です。20~30GB程度のメモリを一気にアロケーションして一気に計算して一気に解放します。爽快です!気をよくして、主マシンのメモリUP↑計画(256GB化)を前倒そうと思います。

測定

Java

(List)

Python※

(Pypy)

JavaScript

(Node.js)

Rust C++
データ準備(sec) 8.107

184.913

(112.408)

エラー発生!

※1億個だと30.892

 

1.158 1.408

データ集計

(sec)

1.966

12.420

(1.343)

※1億個だと

0.555

0.464 0.429
所感

善戦だと思います。

マルチスレッド

します。

通常配列は不得意。

メモリ系エラー 爆速です。 爆速です。

※PythonはList([])を使用しました。

※JavaScriptはエラーが発生しました。heapを32GBに設定して1億くらいまではいけたのですが、配列数が多すぎて Fatal JavaScript invalid size errorお亡くなり

十分速くていい感じなのですが、メモリを増強してオブジェクトを160億個(ただし分散/永続化含む)まで作成するので、いまのところJavaScriptは対象外とします。。。

JavaScript (オブジェクト1億まで):メインは1スレッドのようですが、他3~4スレッドが補助しているようです。(おそらくはGC系か?)

他言語のCPU負荷は前回とおんなじ感じなので省略します。

その他:

Javaはvectorが正式に非推奨になっている訳ではないようですが、公式に「vectorではなくlistを使用するよう推奨する」と記載があるのでList/ArrayListで確認しました。(確かに2倍くらいの速度差がありました。)そういえばjavaでvectorは使ったことがなかったです。

 

(蛇足)ソース

ソースは前回とほぼ同じなので、貼るまでもないのですが、1年後くらいにあれ?どうだっけ?と気になりそうなので、いちおう変更分を貼っておきます。

 

Java

import java.util.ArrayList;import java.util.List;


List<Neuron> nList = new ArrayList<Neuron>(NEURO_COUT);


for (Integer i = 0; i < NEURO_COUT; i++) {
    Neuron nr = new Neuron();
    nr.setActivate(i);
    nr.setBias(1.01);
    nList.add(nr);

}


for (Integer i = 0; i < NEURO_COUT; i++) {
    Neuron nr = nList.get(i);
    sum += nr.getActivate();
    dummy += nr.getBias();
}

Python

# 初期化は固定値にしてみる
neuro_array = [ Neuro(bias=1.01, activate=1.01)] * NEURO_COUNT
# 初期化してあるので、appendでなく置き換えられる(appendは超絶遅いので使わない)
for i in range(NEURO_COUNT):
neuro_array[i] = Neuro(bias=1.01, activate=float(i))
# データを件数分加算
for i in range(NEURO_COUNT):
sum += neuro_array[i].activate
dummy += neuro_array[i].bias

JavaScript:ぜんぶはります

const NEURO_COUNT = 100_000_000;

const neuro_class = class {
    constructor(bias, activate) {
        this.bias = bias;
        this.activate = activate;
    }
}

console.log('Hello World!(Java Script)');
const startTime = performance.now();

let n_ary = []
for(let i = 0; i < NEURO_COUNT; i++) {
    // JavaScriptは初期化してなくてもいきなり添え字指定で代入できる
    n_ary[i] = new neuro_class(1.01, i);
}
const endTime = performance.now();
console.log(`データ生成処理時間 = ${(endTime - startTime)/1000} sec`);
let sum = 0.0;
let dummy = 0.0;
const startTime2 = performance.now();
for(let i = 0; i < NEURO_COUNT; i++) {
    sum += n_ary[i].activate;
    dummy += n_ary[i].bias;
}
const endTime2 = performance.now();
console.log(`sum = ${sum}`);
console.log(`dummy = ${dummy}`);
console.log(`データ集計処理時間 = ${(endTime2 - startTime2)/1000} sec`);

2億個のオブジェクトは無理でした

PS C:\work\node\test01> node  --max-heap-size=32768 test01.js
Hello World!(Java Script)

#
# Fatal error in , line 0
# Fatal JavaScript invalid size error 169220804
#
#FailureMessage Object: 00000086483FEA80       
 1: 00007FF76F36D51F node_api_throw_syntax_error+175743
 2: 00007FF76F2837BF v8::CTypeInfoBuilder<void>::Build+12703
 3: 00007FF77012D852 V8_Fatal+162
 4: 00007FF76FC3AB05 v8::internal::FactoryBase<v8::internal::Factory>::NewFixedArray+101
 5: 00007FF76FABFF43 v8::internal::FeedbackNexus::ic_state+65795
 6: 00007FF76FADEAC0 v8::Message::GetIsolate+15600
 7: 00007FF76F92AE10 v8::internal::CompilationCache::IsEnabledScriptAndEval+25952       
 8: 00007FF76FE38051 v8::internal::SetupIsolateDelegate::SetupHeap+558193
 9: 00007FF6EFF8F6BC 
PS C:\work\node\test01> 

Rust

let mut n_vec: Vec<Neuron> = Vec::with_capacity(NEURO_COUT);

   for i in 0..NEURO_COUT {
        n_vec.push(Neuron{bias: 1.01 as f64, activate: i as f64});
    }

    for i in 0..NEURO_COUT {
        sum +=  n_vec[i].activate;
        dummy += n_vec[i].bias;
    }

C++

    std::vector<Neuron> nr_vec(NEURO_COUNT);

    for (int i = 0; i < NEURO_COUNT; i++) {
        Neuron n = { 1.01, i };
        nr_vec[i] = n;
    }

    for (int i = 0; i < NEURO_COUNT; i++) {
        sum += nr_vec[i].activate;
        dummy += nr_vec[i].bias;
    }