いつから?どうしてこうなった~?
こんにちわ、こんばんわ。かえるのクーの助手の「井戸中 聖」(いとなか セイ)でございます。
たまたまJavaで試作していたプログラムが、Javaにしては非常識に速いので、他の言語と比べてみることにしました。
比較のため、ポイントにだけにそぎ落としています
・簡単なオブジェクト(インスタンス)を4億個準備する。(パラメータ値あり)
・オブジェクトをハッシュ(もしくは連想配列)に格納する。
・上記データの準備にどれくらいかかるか測定する。
・キャッシュの影響をすくなくするため、そのまま30秒待つ
・4億個のインスタンスを順次取得してパラメータ値を加算する。
・加算した値を表示する。
・加算にかかる時間を計測する。
結果:
測定 |
Java |
Python※ |
Python※
(Pypy)
|
Rust |
C/C++ |
データ準備(sec) |
5.392 |
179.065 |
101.179 |
79.219 |
166.923 |
データ集計
(sec)
|
1.619 |
18.055 |
2.269 |
61.565 |
88.702 |
所感 |
えっ ?!?! |
よくできました? |
まぁまぁ!
(元々Pythonの2~10倍速い感触)
|
優秀なはずでは? |
連想配列苦手? |
※Pythonではメモリ不足でswapが発生するので半分の2億個にしています。
もちろん、数字が小さい方が速いんですよ!(数字と言語みると誤解しそう。)
Javaのデータ準備が10Sec以内なのは異常。並列処理でもしない限り無理!→並列処理してるっぽいです。
Java:本当に全コア使って総力戦してます。。。これなら10Sec切るかぁ。でもHadoop / spark もThreadも使ってないんだよ。(sleepにはThread使ってるけどぉ)
Python:100%近くとなっているのは、その時点でどれか1個だけ。Pythonですから。
numpy使ってませんから。。
Rust:100%近くとなっているのは、その時点でどれか1個だけ。マルチスレッドコーディングしてませんから。(もちろんリリース/最高オプティマイズでの実行です)
Rustの連想配列って、Pythonより遅いんだ。。。がんばれ!
Rustが予想外に(集計が)遅いので、C / C++でやってみたくなりました。
今使ってないんで、やるならVisualStudio入れるところからです。
(追記)速度差についてはRustが遅いんではなくて、JavaやPythonの連想配列が異常に速いことに起因しているようです。それにしてもJavaの速さは異常。。。そして何故に指定もしないのにマルチスレッド。。。
C++:そういえばC++知らないんでした。べたべたで作りました。もちろんシングルスレッドです。(リリースコンパイル実行です)
ちなみにC++で連想配列でなく、普通の配列では4億個は上限越えでNGでした。
(コンパイル上限近くの)要素数が2億6800万個では、データ準備で0.738sec 、データ集計で0.337sec でした。
今回は比較が連想配列縛りなので、悪しからず。。。ちなみに何をもって配列上限としているのかは不明でした。(追記ここまで)
Javaをサンマイクロシステムズ社が健在なころから、ずっと使ってます。何も指定しなければ&マルチスレッド系ライブラリを使用しなければ、主処理はシングルスレッド動作でしか動作しない認識でした。まぁ、最近のSpring/Web系のコンテナで動かすとマルチスレッドなこともありますが。。。今回は純粋なJava/main()です!
今回確認したJavaはJava17(OpenJDK 17.0.6 /2023-01-17)です。
ネットにも「勝手に」マルチスレッド動作する記事ないし。。。調査が必要です。
結論
Javaのベンチマークで、c++の2倍を切ってそこそこ早い記事を見て、昔からの遅いJavaを知っている身としては「眉唾だなぁ」と思ってました。
【Rust】Rust最速物語を他の言語と比較して検証してみたぴょい - Rのつく財団入り口 (hatenablog.com)
宇宙の晴れ上がり: プログラミング言語の実行速度比較(2023/5) (transparent-to-radiation.blogspot.com)
Java超絶早くなってるじゃん。ネイティブコンパイラに「そんなに」ひけをとらないってどういう頑張り??あと、JavaScript(Node.js)が超絶早いのは何故?
やっぱり、この世界は元いた世界と違うのか。。(個人的に昨年異世界転生の疑惑あり)
(蛇足)
あっこちゃん来もせず、用もないのに納豆売りがぁ~♪あ~あ~あぁナット~
(蛇足のソース)
Java
MainClass.java
package artifact;
import java.util.HashMap;
import java.util.Map;
public class MainClass {
final static int NEURO_COUT = 400000000;
public static void main(String[] args) {
System.out.println("Hello World!(Java)");
Runtime rt = Runtime.getRuntime();
// reportMemory(rt);
long startTime = System.currentTimeMillis();
Map<Integer, Neuron> nMap = new HashMap<Integer, Neuron>();
// データをNEURO_COUT件準備 JavaではclassをNewして準備
for (Integer i = 0; i < NEURO_COUT; i++) {
Neuron nr = new Neuron();
nr.setActivate(i);
nr.setBias(1.01);
nMap.put(i, nr);
}
long endTime = System.currentTimeMillis();
System.out.println(String.format("データ生成処理時間:%.3f sec" ,(endTime - startTime)/1000.0));
// キャッシュ等抑制のため開始を少し待つ(おまじない)
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
}
double sum = 0.0;
startTime = System.currentTimeMillis();
System.out.println("calc start");
// データを件数分加算
for (Integer i = 0; i < NEURO_COUT; i++) {
Neuron nr = nMap.get(i);
sum += nr.getActivate();
}
endTime = System.currentTimeMillis();
System.out.println("sum: " + sum);
System.out.println(String.format("データ集計処理時間:%.3f sec" , (endTime - startTime)/1000.0));
reportMemory(rt);
}
private static void reportMemory(Runtime rt) {
System.out.println("=== Memory ===");
System.out.println(String.format("total: %,d bytes",rt.totalMemory()));
System.out.println(String.format("free : %,d bytes" ,rt.freeMemory()));
System.out.println(String.format("max : %,d bytes" ,rt.maxMemory()));
}
}
Neuron.java (get/setはlombokを使ってます)
package artifact;
import lombok.Data;
@Data
public class Neuron {
double bias;
double activate;
}
Python
# -*- coding: utf-8 -*-
import time
import psutil #Pypyではコメントアウトのこと
NEURO_COUNT = 200000000;
class Neuro():
def __init__(self,bias=1.0, activate=1.0):
self.bias = bias
self.activate = activate
def main():
print("Helo, world!(Python)")
# print(psutil.virtual_memory())
time_start = time.perf_counter()
hash_dict = {}
# データをNEURO_COUT件準備 Pythonではclassからインスタンス生成をして準備
for i in range(NEURO_COUNT):
hash_dict[i] = Neuro(bias=1.01, activate=float(i))
time_end = time.perf_counter()
print("データ生成処理時間: %.3f sec" % (time_end - time_start))
# キャッシュ等抑制のため開始を少し待つ(おまじない)
time.sleep(30)
sum: float = 0.0
print("calc start")
time_start = time.perf_counter()
# データを件数分加算
for i in range(NEURO_COUNT):
sum += hash_dict[i].activate
time_end = time.perf_counter()
print("sum: %e" % sum)
print("データ集計処理時間: %.3f sec" % (time_end - time_start))
print(psutil.virtual_memory())
if __name__ == "__main__":
main()
Rust
use std::collections::HashMap;
use std::time::{Duration, Instant};
use std::thread::sleep;
use sysinfo::{System, SystemExt};
const NEURO_COUT:i32 = 400_000_000;
struct Neuron {
bias: f64,
activate: f64,
}
fn main() {
println!("Hello, world!(Rust)");
// report_memory();
let mut now = Instant::now();
let mut n_map = HashMap::new();
// データをNEURO_COUT件準備 JavaではclassをNewして準備
for i in 0..NEURO_COUT {
n_map.insert(i, Neuron{bias: 1.01 as f64, activate: i as f64});
}
let end_time = now.elapsed();
println!("データ生成処理時間:{:?}", end_time);
// キャッシュ等抑制のため開始を少し待つ(おまじない)
sleep(Duration::from_millis(30000));
let mut sum:f64 = 0.0;
now = Instant::now();
println!("calc start");
// データを件数分加算
for i in 0..NEURO_COUT {
sum += n_map[&i].activate;
}
let end_time = now.elapsed();
println!("sum:{:?}", sum);
println!("データ集計処理時間:{:?}", end_time);
report_memory();
}
fn report_memory() {
let mut sys = System::new_all();
sys.refresh_all();
println!("=> system:");
// RAM and swap information:
println!("total memory: {} bytes", sys.total_memory());
println!("used memory : {} bytes", sys.used_memory());
println!("total swap : {} bytes", sys.total_swap());
println!("used swap : {} bytes", sys.used_swap());
}
C++
#include <iostream>
#include <unordered_map>
#include <string>
#include <chrono>
#include <thread>
#include <windows.h> //OS依存
#include <psapi.h>
#include <time.h>
# define NEURO_COUNT 400000000
struct Neuron {
double bias;
double activate;
};
class ReportMem
{
public:
void report_memory();
};
void ReportMem::report_memory()
{
HANDLE hProc = GetCurrentProcess();
PROCESS_MEMORY_COUNTERS_EX pmc;
BOOL isSuccess = GetProcessMemoryInfo(
hProc,
(PROCESS_MEMORY_COUNTERS*)&pmc,
sizeof(pmc));
CloseHandle(hProc);
std::cout << "Mem:PrivateUsage=" << pmc.PrivateUsage << std::endl;
}
int main()
{
std::cout << "Hello World!(C++)\n";
clock_t start_time = clock();
std::unordered_map<std::string, Neuron> n_map;
std::string str;
for (int i = 0; i < NEURO_COUNT; i++) {
Neuron n = { 1.01, i };
str = std::to_string(i);
n_map[str] = n;
}
clock_t end_time = clock();
std::cout << "データ生成処理時間 = " << (double)(end_time - start_time) / CLOCKS_PER_SEC << "sec.\n";
std::this_thread::sleep_for(std::chrono::milliseconds(30000)); //30秒間スリープします
start_time = clock();
double sum = 0.0;
std::cout << "calc start\n";
for (int i = 0; i < NEURO_COUNT; i++) {
str = std::to_string(i);
sum += n_map[str].activate;
}
end_time = clock();
std::cout << "sum = " << sum << "\n";
std::cout << "データ集計処理時間 = " << (double)(end_time - start_time) / CLOCKS_PER_SEC << "sec.\n";
ReportMem rm;
rm.report_memory();
}