みなさんプログラミングの世界に足を踏み入れると、様々な専門用語に出会うことがありますね?その中でも特に重要な概念の一つが「型安全」と「コンパイル方式」というものです。
今回の記事では、型安全とは何か、そしてそれが実行速度にどのように影響するのか、また、それぞれのプログラミングにおいて同じ処理をどのくらいの時間で実行できるかを確かめていこうと思います。
ついでにtsgo(TypeScript7.0)の実行時間(コンパイル時間)も調べてみようと思います。
次回の記事でTS7.0は深掘りしようと思います.今回は簡単な紹介で...
実行環境
- macOS Sonoma 14.5.0 (ARM64)
- Darwin 24.5.0 (arm64)
以下,実行環境等のバージョンです。インストールしていないものがある場合は事前にインストールして欲しいです
- Node.js: v23.11.0
- TypeScript: 5.8.3 (tsx、ts-node用)
- TypeScript Native Preview: 7.0.0-dev.20250712.1 (実験的プレビュー版、Go言語実装)
- tsx: v4.20.3
- ts-node: v10.9.2
- Bun: 1.2.14
- C++: Apple clang 17.0.0, GCC 15.1.0 (Homebrew)
- Java: OpenJDK 24.0.1
- C#: .NET 9.0.202
- Swift: 6.0.3
- Rust: 1.88.0 (rustc), Cargo 1.88.0
- Go: 1.24.5
- Ruby: 3.0.2p107
- Python: 3.11.6
- PHP: 8.4.10
TypeScriptで学ぶプログラミング言語の世界
Part 1 手続型からオブジェクト指向へ
Part2 ORMってなんなんだ?SQLとオブジェクト指向のミスマッチを感じませんか?
Part3 プログラミングパラダイムの進化と革命:機械語からマルチパラダイムへ...新しいプログラミング言語に出会ってみよう!
Part4 アクセス修飾子とは? public? private? protected?
Part5 総称型(ジェネリクス・型パラメータ)とは?
Part6 staticってなんなんだ?
Part7 アーキテクチャって何だ!?ただ動けばいいコードからの脱出をしよう!(クリーンアーキテクチャ)
他のシリーズ記事
TypeScriptを知らない方は以下の記事を参照してください
この記事はチートシートとしてシリーズ化しています.これは様々な言語,フレームワーク,ライブラリなど開発技術の使用方法,基本事項,応用事例を網羅し,手引書として記載したシリーズです.git/gh,lazygit,docker,vim,typescript,プルリクエスト/マークダウン,ステータスコード,ファイル操作,OpenAI AssistantsAPI,Ruby/Ruby on Rails のチートシートがあります.上の記事に遷移した後,各種チートシートのリンクがあります.
情報処理技術者試験合格への道 [IP・SG・FE・AP]
情報処理技術者試験に出題されるコンピュータサイエンス用語の紹介や単語集
IAM AWS User クラウドサービスをフル活用しよう!
AWSのサービスを例にしてバックエンドとインフラ開発の手法を説明するシリーズです.
AWS UserのGCP浮気日記
GCPの様子をAWSと比較して考えてみるシリーズ
Project Gopher: Unlocking Go’s Secrets
Go言語や標準ライブラリの深掘り調査レポート
Gopher’s Journey: Exploring TCP Protocol
Goを用いてTCPプロトコルを探求 & 作成するシリーズです.
型安全って何?
型安全とは、プログラムの実行時に型に起因するエラー(例えば、数値と文字列を誤って結合しようとするなど)が発生しないように設計されたプログラミング言語の特性である。これは、まるで料理の際に「砂糖と塩を間違えないように」ラベルを貼っているようなものだ。これにより、予期せぬ動作やクラッシュを防ぎ、堅牢なアプリケーション開発を促進する。
JavaScriptとTypeScriptの2つの言語を比較しながら、この概念を深く掘り下げていこうと思います。
JavaScript
JavaScriptは「動的型付け言語」に分類される。これは、変数の型が実行時に決定されることを意味する。
let myVariable = "Hello";
myVariable = 123; // エラーにならない
console.log(myVariable + 456); // 579 (数値として結合)
console.log(myVariable + " World"); // 123 World (文字列として結合)
このコードでは、myVariableに最初に文字列"Hello"を代入し、次に数値123を代入している。JavaScriptでは、このような型変更が許容される。そのため、myVariableがどのような型であるかを、コードを読んだだけではすぐに判断できない場合がある。これは柔軟である反面、意図しない型変換や操作によってバグを引き起こす可能性がある。例えば、myVariable + 456が文字列結合を意図しているのに数値演算が行われてしまう、といったことが起こりうる。
TypeScriptにおける型安全
一方で、TypeScriptはJavaScriptに「静的型付け」の概念を導入した言語である。静的型付けとは、変数の型をコンパイル時(コードを実行する前)にチェックすることを意味する。
let myVariable: string = "Hello";
// myVariable = 123; // エラー: 型 'number' を型 'string' に割り当てることはできない
let anotherVariable: number = 123;
// console.log(anotherVariable + " World"); // エラー: 数値と文字列の結合は型エラーになる場合がある (tsconfigの設定による)
このTypeScriptのコードでは、myVariableをstring型として宣言している。もしこれにnumber型の値123を代入しようとすると、TypeScriptのコンパイラがまるで厳格な先生のようにエラーを報告する。同様に、anotherVariableがnumber型であるため、文字列" World"との直接的な結合は型エラーとして指摘される(厳密な設定の場合)。
このように、TypeScriptはコードの記述段階やコンパイル時に型の不整合を検出し、開発者に通知する。これにより、実行時になって初めて型エラーに気づくという状況を避けることができる。これが「型安全」の基本的な考え方です。
tsxでの実行
tsxはTypeScriptの実行環境です。tsxを使用すると、TypeScriptのコードを直接実行することができる。
tsx index.ts
nodeでのTypeScriptのネイティブ実行
nodeでTypeScriptのネイティブ実行をする場合は、以下のコマンドを使用します。
tsxではなくnodeで実行することで、TypeScriptの実行速度が向上します。
node index.ts
nodeでTypeScriptのネイティブ実行をする場合nodeのバージョンは23.6以降である必要があります。
型安全のメリット
型安全な言語の主なメリットは以下の3つです。
- 信頼性の向上: 型エラーによる予期せぬプログラムの停止や誤動作が減り、より堅牢なソフトウェアを開発できる。まるで建物の基礎をしっかりと固めるようなものですね
- 開発効率の向上: 型エラーがコンパイル時に早期発見されるため、デバッグの時間が短縮される。また、IDE(統合開発環境)による補完機能が強化され、まるで優秀なアシスタントがついているかのようにコーディングがスムーズになると思います
- コードの可読性と保守性の向上: 変数や関数の型が明確になるため、コードが読みやすくなり、他の開発者が理解しやすくなる。変更を加える際にも、型チェックによって影響範囲を把握しやすくなり、な安心感を得られるのではないでしょうか
型安全性と実行速度の関係
-
静的型付け言語 (型安全性が高い):
コンパイル時に変数の型が確定しているため、コンパイラは実行効率の良い機械語コードを生成できる。実行時には型チェックのオーバーヘッドがほとんど発生しないため、一般的に高速である。また、コンパイラは型情報に基づいて様々な最適化を行うことが可能 -
動的型付け言語 (型安全性が低い):
実行時に変数の型が決定されるため、その都度型チェックのオーバーヘッドが発生する。また、型が実行時まで確定しないため、コンパイラ(あるいはインタプリタ)が高度な最適化を行うのが難しい場合がある。そのため、静的型付け言語に比べて遅い傾向がある。ただし、最新のランタイム(JavaScriptのV8エンジンなど)はJIT(Just-In-Time)コンパイルなどの技術を用いて、動的型付け言語のパフォーマンスを向上させている場合もある
(おまけ)TypeScript 7.0プレビュー版
TypeScript 7.0プレビュー版(@typescript/native-preview
)は、Microsoftが開発している次世代のTypeScriptコンパイラである。この版は従来のTypeScriptから大幅に書き換えられており、以下の特徴があります:
- Go言語による書き直し: Project Corsaと呼ばれる、Go言語で完全に書き直されたコンパイラ
-
10倍の高速化: 大規模プロジェクトで最大10倍のコンパイル速度向上を実現
- VS Code (150万行): 77.8秒 → 7.5秒(10.4倍)
- Playwright (35万行): 11.1秒 → 1.1秒(10.1倍)
- TypeORM (27万行): 17.5秒 → 1.3秒(13.5倍)
- メモリ効率: 従来の約半分のメモリ使用量を実現
- JSX完全サポート: JSXの解析と型チェックに対応
- JavaScript型チェック: JSDocベースのJavaScript型チェック機能
-
実験的段階:
tsgo
コマンドで提供、最終的にはtsc
に統合予定(2025年末予定)
せっかくなのでtypescriptの7.0プレビュー版も検証してみます。
コンパイル方式って何?
プログラミング言語の実行速度を理解するために、まず「コンパイル方式」について詳しく見ていきましょう。コンパイル方式とは、プログラムのソースコードを機械語(コンピューターが理解できる言語)に変換する方法のことです。
コンパイル方式の基本概念
プログラミング言語で書かれたソースコードは、そのままではコンピューターが理解できません。人間が日本語で書いた文章を、英語を話す人に伝えるためには翻訳が必要なのと同じように、プログラムを実行するためには「翻訳」が必要です。この翻訳の方法によって、プログラムの実行速度が大きく変わってきます。
主なコンパイル方式の種類
1. コンパイル型言語
仕組み: ソースコードを実行前に一括で機械語に変換する方式です。
特徴:
- 事前変換: プログラムを実行する前に、すべてのソースコードを機械語に変換します
- 実行ファイル生成: 変換後は、CPUが直接実行できる実行ファイルが生成されます
- 高速実行: 実行時には翻訳処理が不要なため、非常に高速に動作します
- プラットフォーム依存: 特定のCPUアーキテクチャ向けに最適化されます
例: C++、Rust、Go、C#(一部)
メリット:
- 実行速度が非常に高速
- 最適化が効率的に行われる
- 配布時にソースコードを隠すことができる
デメリット:
- コンパイル時間が必要
- プラットフォームごとに別々のコンパイルが必要
- 開発サイクルが長くなる場合がある
2. インタープリタ型言語
仕組み: ソースコードを実行時に一行ずつ、またはブロックごとに機械語に変換しながら実行する方式です。
特徴:
- 実行時変換: プログラムの実行と同時に翻訳処理を行います
- 即座実行: ソースコードを書いてすぐに実行できます
- 実行時オーバーヘッド: 実行のたびに翻訳処理が発生します
- プラットフォーム独立: インタープリターがあればどの環境でも実行可能
例: Python、Ruby、PHP(従来の実装)
メリット:
- 開発サイクルが高速(コンパイル時間不要)
- デバッグがしやすい
- プラットフォーム独立性が高い
- 動的な機能を実装しやすい
デメリット:
- 実行速度が遅い
- 実行時にソースコードが必要
- 実行時エラーが発生しやすい
3. ハイブリッド型(仮想マシン + JIT)
仕組み: 中間コードにコンパイルした後、実行時に機械語に変換する方式です。
特徴:
- 中間コード生成: まず中間言語(バイトコード)にコンパイルします
- JIT(Just-In-Time)コンパイル: 実行時に機械語に変換します
- 動的最適化: 実行時の情報を使って最適化を行います
- プラットフォーム独立: 仮想マシンがあればどこでも実行可能
例: Java(JVM)、C#(.NET CLR)、JavaScript(V8エンジン)
メリット:
- 実行速度とポータビリティのバランスが良い
- 実行時最適化により、場合によってはネイティブコードと同等の性能
- 一度コンパイルすれば複数のプラットフォームで実行可能
デメリット:
- 初回実行時のウォームアップ時間が必要
- 仮想マシンが必要
- メモリ使用量が多くなる場合がある
実行速度への影響
コンパイル方式による実行速度の一般的な傾向:
コンパイル型 > ハイブリッド型 > インタープリタ型
ただし、現代の技術では:
- V8エンジン(JavaScript): 高度なJIT最適化により、コンパイル型言語に匹敵する性能
- JVM(Java): 長時間実行では、ネイティブコードよりも高速になることもある
- PyPy(Python): JITコンパイラにより、標準Pythonの数倍から数十倍高速
TypeScriptの特殊な位置づけ
TypeScriptは少し特殊な位置にあります:
- トランスパイル: TypeScriptからJavaScriptへの変換(型情報の除去)
- JavaScript実行: V8エンジンでのJIT実行
この二段階の処理により、TypeScriptは:
- 開発時: 型安全性の恩恵を受けられる
- 実行時: JavaScriptと同等の高速性を実現
【実験】様々な言語での実行速度比較
ここでは、計算量の多い素数判定のコードを様々な言語で記述し、それぞれの実行方法と速度傾向を考察する。目標は100万までの素数をすべて見つけることだ。まるで宝探しゲームのように、各言語がどれだけ効率よく素数という宝物を見つけられるかを競い合ってみよう。
以下のコマンドは実行するファイルを生成するコマンドです。これで一括で色々生成できます。
touch prime_finder.ts prime_finder.cpp PrimeFinder.java prime_finder.cs prime_finder.swift prime_finder.rs prime_finder.go prime_finder.js prime_finder.rb prime_finder.py prime_finder.php
TypeScript
言語の特徴: TypeScriptはMicrosoftが開発したJavaScriptのスーパーセット。JavaScriptに型システムを追加することで、大規模なWebアプリケーション開発をより安全に行えるようになった。既存のJavaScriptコードと完全に互換性があるため、段階的な移行が可能
型安全: 高い(静的型付け、コンパイル時に型チェック)
実行方式: 直接実行可能(tsx、ts-node、Node.js 22.6+、Bun、Node.js 23.6+ ネイティブ実行など)
function isPrime(n: number): boolean {
if (n <= 1) return false;
for (let i = 2; i * i <= n; i++) {
if (n % i === 0) return false;
}
return true;
}
const startTime = process.hrtime.bigint();
const limit = 1_000_000;
let count = 0;
for (let i = 2; i <= limit; i++) {
if (isPrime(i)) {
count++;
}
}
const endTime = process.hrtime.bigint();
const duration = Number(endTime - startTime) / 1_000_000; // ミリ秒
console.log(`Found ${count} primes up to ${limit}`);
console.log(`Execution time: ${duration.toFixed(2)} ms`);
実行方法:
# 従来の方法(tsx使用)
npm install -g tsx
tsx prime_finder.ts
# Node.js 23.6以降のネイティブ実行
node prime_finder.ts
# TypeScript 7.0プレビュー版
npm install -D @typescript/native-preview
npx tsgo prime_finder.ts # コンパイル
node prime_finder.js # 実行
今回はjavascriptに手動でトランスパイルして実行しても検証にならないので以下の方法では検証しない。
# TypeScriptコンパイラのインストール(必要に応じて)
npm install -g typescript
# コンパイル
tsc prime_finder.ts
# 実行
node prime_finder.js
C++
言語の特徴: C++は「C言語の拡張」として生まれた、システムプログラミングからアプリケーション開発まで幅広く使われる言語だ。オブジェクト指向プログラミングと低レベルプログラミングの両方をサポートし、C言語レベルの高いパフォーマンスを提供する。メモリ管理を手動で行う必要があるが、その分極めて高速な実行が可能
型安全: 非常に高い(静的型付け)
実行方式: コンパイル型
#include <iostream>
#include <chrono>
using namespace std;
bool isPrime(int n) {
if (n <= 1) return false;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return true;
}
int main() {
auto startTime = chrono::high_resolution_clock::now();
int limit = 1000000;
int count = 0;
for (int i = 2; i <= limit; i++) {
if (isPrime(i)) {
count++;
}
}
auto endTime = chrono::high_resolution_clock::now();
auto duration = chrono::duration_cast<chrono::milliseconds>(endTime - startTime);
cout << "Found " << count << " primes up to " << limit << endl;
cout << "Execution time: " << duration.count() << " ms" << endl;
return 0;
}
実行方法:
g++ prime_finder.cpp -o prime_finder && ./prime_finder
Java
言語の特徴: Javaは「一度書けば、どこでも実行できる」をモットーとする企業向けの言語だ。強い型安全性と豊富なライブラリ、そして長年の実績により、多くの企業システムで採用されている。JVM(Java Virtual Machine)上で動作するため、プラットフォーム間の移植性が高い
型安全: 高い(静的型付け)
実行方式: コンパイル型(JVM上で実行)
public class PrimeFinder {
public static void main(String[] args) {
long startTime = System.nanoTime();
int limit = 1000000;
int count = 0;
for (int i = 2; i <= limit; i++) {
if (isPrime(i)) {
count++;
}
}
long endTime = System.nanoTime();
long duration = (endTime - startTime) / 1_000_000; // ミリ秒
System.out.println("Found " + count + " primes up to " + limit);
System.out.println("Execution time: " + duration + " ms");
}
public static boolean isPrime(int n) {
if (n <= 1) return false;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return true;
}
}
実行方法:
Java 11以降:
java PrimeFinder.java
C#
言語の特徴: C#はMicrosoftが開発した現代的なオブジェクト指向言語だ。Javaに影響を受けつつも、より現代的な言語機能を取り入れている。.NET ecosystem内で強力な統合開発環境とライブラリを提供し、Web開発からデスクトップアプリケーションまで幅広く対応している
型安全: 高い(静的型付け)
実行方式: コンパイル型(.NET CLR上で実行)
using System;
using System.Diagnostics;
public class PrimeFinder
{
public static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
int limit = 1000000;
int count = 0;
for (int i = 2; i <= limit; i++)
{
if (IsPrime(i))
{
count++;
}
}
stopwatch.Stop();
Console.WriteLine($"Found {count} primes up to {limit}");
Console.WriteLine($"Execution time: {stopwatch.ElapsedMilliseconds} ms");
}
public static bool IsPrime(int n)
{
if (n <= 1) return false;
for (int i = 2; i * i <= n; i++)
{
if (n % i == 0) return false;
}
return true;
}
}
実行方法:
# コンパイル&実行
dotnet run
Swift
言語の特徴: SwiftはAppleが開発した現代的なプログラミング言語だ。Objective-Cの後継として位置づけられ、iOS/macOSアプリケーション開発の主力言語となっている。安全性、速度、表現力を重視した設計で、関数型プログラミングの要素も取り入れている
型安全: 高い(静的型付け)
実行方式: コンパイル型
import Foundation
func isPrime(_ n: Int) -> Bool {
if n <= 1 { return false }
var i = 2
while i * i <= n {
if n % i == 0 { return false }
i += 1
}
return true
}
let startTime = Date()
let limit = 1_000_000
var count = 0
for i in 2...limit {
if isPrime(i) {
count += 1
}
}
let endTime = Date()
let duration = endTime.timeIntervalSince(startTime) * 1000 // ミリ秒
print("Found \(count) primes up to \(limit)")
print("Execution time: \(Int(duration)) ms")
実行方法:
# コンパイル&実行
swift prime_finder.swift
Rust
言語の特徴: Rustは「速度、安全性、並行性」を三つの柱とするシステムプログラミング言語だ。C/C++に匹敵する性能を持ちながら、メモリ安全性を保証する画期的な言語である。所有権システムという独自の概念により、ガベージコレクションなしにメモリ安全性を実現している
型安全: 非常に高い(静的型付け、メモリ安全性も高い)
実行方式: コンパイル型
use std::time::Instant;
fn is_prime(n: u32) -> bool {
if n <= 1 { return false; }
let mut i = 2;
while i * i <= n {
if n % i == 0 { return false; }
i += 1;
}
true
}
fn main() {
let start_time = Instant::now();
let limit = 1_000_000;
let mut count = 0;
for i in 2..=limit {
if is_prime(i) {
count += 1;
}
}
let end_time = Instant::now();
let duration = end_time.duration_since(start_time);
println!("Found {} primes up to {}", count, limit);
println!("Execution time: {} ms", duration.as_millis());
}
実行方法:
# プロジェクトを作成
cargo new prime_finder
cd prime_finder
# src/main.rsにコードを記述後、実行
cargo run
Go
言語の特徴: GoはGoogleが開発したシンプルで実用的な言語。「少ない機能で多くのことを成し遂げる」という哲学の下、並行処理(goroutine)を言語レベルでサポートし、クラウドサービスやマイクロサービスの開発に最適化されている。コンパイル速度の速さも特徴の一つ。
型安全: 高い(静的型付け)
実行方式: コンパイル型
package main
import (
"fmt"
"time"
)
func isPrime(n int) bool {
if n <= 1 {
return false
}
for i := 2; i*i <= n; i++ {
if n%i == 0 {
return false
}
}
return true
}
func main() {
startTime := time.Now()
limit := 1000000
count := 0
for i := 2; i <= limit; i++ {
if isPrime(i) {
count++
}
}
endTime := time.Now()
duration := endTime.Sub(startTime)
fmt.Printf("Found %d primes up to %d\n", count, limit)
fmt.Printf("Execution time: %d ms\n", duration.Milliseconds())
}
実行方法:
go run prime_finder.go
JavaScript
言語の特徴: JavaScriptは元々ウェブブラウザ用のスクリプト言語として生まれたが、今やフロントエンド、バックエンド、モバイルアプリ開発まで幅広く使われている。動的型付けによる柔軟性と、V8エンジンなどの高性能なランタイムにより、現代のウェブ開発には欠かせない言語。
型安全: 低い(動的型付け)
実行方式: インタプリタ型(Node.jsのV8エンジンはJITコンパイルを行う)
function isPrime(n) {
if (n <= 1) return false;
for (let i = 2; i * i <= n; i++) {
if (n % i === 0) return false;
}
return true;
}
const startTime = process.hrtime.bigint();
const limit = 1_000_000;
let count = 0;
for (let i = 2; i <= limit; i++) {
if (isPrime(i)) {
count++;
}
}
const endTime = process.hrtime.bigint();
const duration = Number(endTime - startTime) / 1_000_000; // ミリ秒
console.log(`Found ${count} primes up to ${limit}`);
console.log(`Execution time: ${duration.toFixed(2)} ms`);
実行方法:
# Node.jsで実行
node prime_finder.js
Ruby
言語の特徴: Rubyは「プログラマーの幸福」を重視して設計された言語。読みやすく書きやすい構文、豊富なメタプログラミング機能により、webアプリケーション開発(Ruby on Rails)で使用される言語で有名。「楽しくプログラミングする」ことを重視した設計思想が特徴的。
型安全: 低い(動的型付け)
実行方式: インタプリタ型
def is_prime(n)
return false if n <= 1
i = 2
while i * i <= n
return false if n % i == 0
i += 1
end
true
end
start_time = Time.now
limit = 1_000_000
count = 0
(2..limit).each do |i|
if is_prime(i)
count += 1
end
end
end_time = Time.now
duration = (end_time - start_time) * 1000 # ミリ秒
puts "Found #{count} primes up to #{limit}"
puts "Execution time: #{duration.to_i} ms"
実行方法:
# Rubyで実行
ruby prime_finder.rb
Python
言語の特徴: Pythonは「読みやすさ」を最重要視した言語。シンプルな構文とバッテリー付属的な豊富な標準ライブラリにより、初心者から上級者まで幅広く愛用されている。データサイエンス、機械学習、Web開発、自動化など、あらゆる分野で活躍している万能選手。
型安全: 低い(動的型付け)
実行方式: インタプリタ型(一部の実行環境ではJITコンパイルが行われることもある)
import time
def is_prime(n):
if n <= 1:
return False
i = 2
while i * i <= n:
if n % i == 0:
return False
i += 1
return True
start_time = time.time()
limit = 1_000_000
count = 0
for i in range(2, limit + 1):
if is_prime(i):
count += 1
end_time = time.time()
duration = (end_time - start_time) * 1000 # ミリ秒
print(f"Found {count} primes up to {limit}")
print(f"Execution time: {duration:.2f} ms")
実行方法:
# Pythonで実行
python prime_finder.py
PHP
言語の特徴: PHPは「Personal Home Page」から「PHP: Hypertext Preprocessor」へと発展した、ウェブ開発に特化した言語だ。サーバーサイドWebアプリケーション開発において長年の実績を持ち、WordPressなどの人気CMSのベースとなっている。最新版では型宣言やJITコンパイルなど、モダンな機能も取り入れている。
型安全: 低い(動的型付け、ただし型ヒントあり)
実行方式: インタプリタ型(Zend EngineはJITコンパイル機能を導入)
<?php
function isPrime(int $n): bool {
if ($n <= 1) return false;
for ($i = 2; $i * $i <= $n; $i++) {
if ($n % $i === 0) return false;
}
return true;
}
$startTime = microtime(true);
$limit = 1_000_000;
$count = 0;
for ($i = 2; $i <= $limit; $i++) {
if (isPrime($i)) {
$count++;
}
}
$endTime = microtime(true);
$duration = ($endTime - $startTime) * 1000; // ミリ秒
echo "Found {$count} primes up to {$limit}\n";
echo "Execution time: " . round($duration, 2) . " ms\n";
?>
実行方法:
# PHPで実行
php prime_finder.php
実行してみた結果
各言語で100万までの素数を見つける処理を10回実行し、その実行時間を測定してみた。
Rust (cargo run --release)
Run 1: 49 ms Run 2: 48 ms Run 3: 49 ms Run 4: 50 ms Run 5: 48 ms
Run 6: 48 ms Run 7: 48 ms Run 8: 48 ms Run 9: 48 ms Run 10: 49 ms
平均: 48.7 ms
C++ (g++ -O2)
Run 1: 50 ms Run 2: 57 ms Run 3: 58 ms Run 4: 63 ms Run 5: 72 ms
Run 6: 53 ms Run 7: 57 ms Run 8: 57 ms Run 9: 61 ms Run 10: 61 ms
平均: 58.9 ms
Go (go run)
Run 1: 58 ms Run 2: 58 ms Run 3: 57 ms Run 4: 57 ms Run 5: 58 ms
Run 6: 58 ms Run 7: 58 ms Run 8: 57 ms Run 9: 58 ms Run 10: 58 ms
平均: 57.7 ms
JavaScript (node)
Run 1: 78.57 ms Run 2: 75.88 ms Run 3: 75.76 ms Run 4: 77.06 ms Run 5: 77.12 ms
Run 6: 76.78 ms Run 7: 77.16 ms Run 8: 77.93 ms Run 9: 77.19 ms Run 10: 79.26 ms
平均: 77.27 ms
TypeScript (tsx)
Run 1: 77.16 ms Run 2: 77.64 ms Run 3: 78.42 ms Run 4: 80.95 ms Run 5: 79.60 ms
Run 6: 78.56 ms Run 7: 81.64 ms Run 8: 79.16 ms Run 9: 81.16 ms Run 10: 79.31 ms
平均: 79.36 ms
TypeScript (Node.js 23 ネイティブ実行)
Run 1: 77.52 ms Run 2: 75.19 ms Run 3: 75.85 ms Run 4: 75.34 ms Run 5: 75.36 ms
Run 6: 75.98 ms Run 7: 76.97 ms Run 8: 76.43 ms Run 9: 77.03 ms Run 10: 77.38 ms
平均: 76.31 ms
TypeScript 7.0プレビュー版 (tsgo → node)
Run 1: 76.47 ms Run 2: 77.73 ms Run 3: 79.19 ms Run 4: 78.86 ms Run 5: 79.43 ms
Run 6: 79.58 ms Run 7: 79.26 ms Run 8: 79.61 ms Run 9: 79.73 ms Run 10: 79.58 ms
平均: 78.94 ms
コンパイル時間測定のため--extendedDiagnosticsを使用
TypeScriptに関してコンパイル時間を測定するために、tscとtsgoの両方でコンパイル時間を測定してみました。
tscのコンパイル時間
❯ npx tsc prime_finder.ts --extendedDiagnostics
Files: 149
Lines of Library: 40506
Lines of Definitions: 54710
Lines of TypeScript: 22
Lines of JavaScript: 0
Lines of JSON: 0
Lines of Other: 0
Identifiers: 83588
Symbols: 105731
Types: 43486
Instantiations: 46664
Memory used: 178011K
Assignability cache size: 17462
Identity cache size: 119
Subtype cache size: 1
Strict subtype cache size: 4
I/O Read time: 0.03s
Parse time: 0.12s
ResolveTypeReference time: 0.00s
ResolveModule time: 0.01s
ResolveLibrary time: 0.00s
Program time: 0.18s
Bind time: 0.06s
Check time: 0.52s
transformTime time: 0.00s
commentTime time: 0.00s
I/O Write time: 0.00s
printTime time: 0.01s
Emit time: 0.01s
Total time: 0.76s
tsgoのコンパイル時間
❯ npx tsgo prime_finder.ts --extendedDiagnostics
Files: 149
Lines: 95238
Identifiers: 83588
Symbols: 114547
Types: 53479
Instantiations: 50447
Memory used: 112840K
Memory allocs: 836135
Parse time: 0.028s
Bind time: 0.006s
Check time: 0.148s
Emit time: 0.001s
Total time: 0.188s
今回のような短いコードではあまり差が出ないので、次回の記事では、TypeScript 7.0プレビュー版のコンパイル速度をさらに検証していきます。
Swift (swift)
Run 1: 99 ms Run 2: 99 ms Run 3: 100 ms Run 4: 99 ms Run 5: 99 ms
Run 6: 99 ms Run 7: 98 ms Run 8: 98 ms Run 9: 98 ms Run 10: 102 ms
平均: 99.1 ms
C# (dotnet run)
Run 1: 117 ms Run 2: 105 ms Run 3: 110 ms Run 4: 106 ms Run 5: 103 ms
Run 6: 108 ms Run 7: 111 ms Run 8: 110 ms Run 9: 101 ms Run 10: 112 ms
平均: 108.3 ms
Java (java)
Run 1: 84 ms Run 2: 137 ms Run 3: 82 ms Run 4: 136 ms Run 5: 138 ms
Run 6: 135 ms Run 7: 138 ms Run 8: 136 ms Run 9: 138 ms Run 10: 82 ms
平均: 120.6 ms(JVM ウォームアップの影響で変動が大きいことを考慮)
PHP (php)
Run 1: 1636.23 ms Run 2: 1476.58 ms Run 3: 1834.76 ms Run 4: 1562.04 ms Run 5: 1437.30 ms
Run 6: 1509.07 ms Run 7: 1384.33 ms Run 8: 1449.56 ms Run 9: 1236.18 ms Run 10: 1435.66 ms
平均: 1496.17 ms
Ruby (ruby)
Run 1: 1787 ms Run 2: 1863 ms Run 3: 1779 ms Run 4: 1784 ms Run 5: 1791 ms
Run 6: 1786 ms Run 7: 1853 ms Run 8: 1785 ms Run 9: 1799 ms Run 10: 1782 ms
平均: 1800.9 ms
Python (python3)
Run 1: 1988.90 ms Run 2: 1989.51 ms Run 3: 1990.78 ms Run 4: 1990.72 ms Run 5: 1984.21 ms
Run 6: 2081.61 ms Run 7: 1982.36 ms Run 8: 1985.32 ms Run 9: 1985.50 ms Run 10: 1971.04 ms
平均: 1990.0 ms
実測結果のまとめ
実際の測定結果では、このような順位となりました:
- Rust: 48.7 ms(最速!メモリ安全性と高速性を両立)
- Go: 57.7 ms(シンプルで高速なコンパイル型言語)
- C++: 58.9 ms(従来の高性能言語の代表)
- TypeScript (Node.js 23ネイティブ実行): 76.31 ms(最新のネイティブTypeScript実行)
- JavaScript: 77.27 ms(V8エンジンの最適化が効いている)
- TypeScript (tsx): 79.36 ms(外部ツールによる型安全な実行)
- TypeScript 7.0プレビュー版: 78.94 ms(次世代コンパイラ、Go製、コンパイル速度は速い)
- Swift: 99.1 ms(Apple製の現代的な言語)
- C#: 108.3 ms(.NET上での安定した性能)
- Java: 120.6 ms(JVMのウォームアップによる変動あり)
- PHP: 1496.17 ms(JITコンパイル有効でも計算処理は苦手)
- Ruby: 1800.9 ms(開発効率重視の動的言語)
- Python: 1990.0 ms(可読性重視、計算処理には向かない)
実行結果の詳細考察
型安全性と実行速度の関係性
今回の測定結果から、型安全性と実行速度の関係は単純ではないことが明らかになりました。
📊 型安全性による分類
高い型安全性(静的型付け):
- Rust: 48.7 ms
- Go: 57.7 ms
- C++: 58.9 ms
- TypeScript: 79.36 ms(tsx)
- Swift: 99.1 ms
- C#: 108.3 ms
- Java: 120.6 ms
低い型安全性(動的型付け):
- JavaScript: 77.27 ms
- PHP: 1496.17 ms
- Ruby: 1800.9 ms
- Python: 1990.0 ms
コンパイル方式による分析
コンパイル方式による分析をしてみました。
その結果、型安全かどうかよりも、コンパイル方式による速度の差の方が大きいことがわかりました。
🏗️ ネイティブコンパイル型(機械語直接生成)
Rust(48.7 ms), Go(57.7 ms), C++(58.9 ms) が圧倒的な速度を記録。これらは事前に最適化された機械語にコンパイルされるため、実行時のオーバーヘッドが最小限です。
🔄 中間コード+JIT型
Java(120.6 ms), C#(108.3 ms) は中間言語(バイトコード/IL)にコンパイル後、JVMやCLRでJITコンパイルされます。ウォームアップ時間が必要ですが、長時間実行では高速化される特性があります。
⚡ JITエンジン型
JavaScript(77.27 ms), TypeScript(79.36 ms) はV8エンジンの高度なJIT最適化により、驚異的な速度を実現。動的型付けでありながら静的型付け言語に匹敵する性能です。
🐌 インタープリター型
Python(1990.0 ms), Ruby(1800.9 ms), PHP(1496.17 ms) は実行時解釈のため大幅に遅い結果となりました。
TypeScriptがJavaScriptより遅い理由
TypeScript: 79.36 ms vs JavaScript: 77.27 ms vs Node.js 23ネイティブ実行: 76.31 ms
この結果は一見逆説的ですが、以下の理由が考えられる:
- トランスパイルオーバーヘッド: TypeScriptは実行時にJavaScriptに変換されるため、わずかな変換コストが発生
- 型チェック情報の残存: コンパイル後のJavaScriptに型チェックのためのコードが一部残存
- V8の最適化: ピュアなJavaScriptの方がV8エンジンの最適化アルゴリズムに適している可能性
- 測定誤差の範囲: 平均差は約2msで、統計的には誤差の範囲内
Node.js 23ネイティブ実行が最も高速な理由:
- Type Stripping効率: SWCベースのType Strippingがtsxよりも効率的
- 最適化されたパース: Node.js内部でのTypeScript処理が最適化されている
- オーバーヘッドの削減: 外部ツールを経由しない直接実行によるオーバーヘッドの削減
各言語の特性別考察
各言語の特性に合わせて実行時間に関する考察をしてみました
🥇 システムプログラミング言語群(Rust, Go, C++)
- 共通点: ネイティブコンパイル、手動メモリ管理または効率的GC
- Rust最速の理由: ゼロコスト抽象化と最新の最適化技術
- Go高速の理由: シンプルな設計とガベージコレクションの効率化
- C++の位置: 歴史ある言語だが、手動最適化の余地が大きい
🥈 モダンスクリプト言語群(JavaScript, TypeScript)
- 驚異的な結果: V8エンジンのJIT最適化により、コンパイル型言語に迫る性能
- Node.js 23ネイティブ実行: 76.31 ms(最も高速なTypeScript実行方法)
- JavaScript: 77.27 ms(標準的なV8実行)
- TypeScript(tsx): 79.36 ms(外部ツールによるオーバーヘッドあり)
- TypeScript 7.0プレビュー版: 79.57 ms(Go製コンパイラによる新時代)
- JavaScript優位: 型情報なしでも実行時プロファイリングによる最適化が効果的
- 実用性: 開発速度と実行速度のバランスが優秀
🥉 エンタープライズ言語群(Swift, C#, Java)
- Swift: Apple製で比較的新しく、最適化が進んでいる
- C#: .NETの成熟したJITにより安定した性能
- Java: JVMウォームアップの影響で変動が大きい(84ms〜138ms)
🏃 Web開発特化言語(PHP)
- PHP: JITエンジン(OPcache)有効でも計算処理は苦手
- 特化領域: Web開発では優秀だが、数値計算には不向き
🐢 スクリプト言語群(Ruby, Python)
- 設計思想: 実行速度より開発効率と可読性を重視
- Ruby: オブジェクト指向の美しさを追求
- Python: 科学計算ライブラリ(NumPy等)使用時は別次元の速度
型安全性の真の価値
今回の結果は、型安全性の価値は実行速度だけでは測れないです
- 開発時の安全性: バグの早期発見
- 保守性: リファクタリングの安全性
- チーム開発: コードの意図の明確化
- 長期プロジェクト: 技術的負債の軽減
動的型付けのJavaScriptが高速だからといって、必ずしも大規模開発でTypeScriptより優れているというわけではありません。用途に応じた適切な言語選択が重要です。
今回の検証により、従来の「型安全 = 高速」という単純な図式では説明できないが、コンパイル方式による速度の差が大きいことがわかりました。
以下のような処理の違いにより使用する言語が変わってくると思います。
計算集約的処理: Rust > Go > C++
Web開発バランス型: TypeScript (Node.js 23ネイティブ実行) > JavaScript > TypeScript (tsx)
エンタープライズ安定性: Java、C#、Swift
プロトタイピング: Python、Ruby、PHP
TypeScript 7.0プレビュー版に関して
今回の検証で明らかになったTypeScript 7.0プレビュー版の特徴は以下の通りみたいです。
コンパイル性能の大幅改善:
- 初回起動のオーバーヘッドを除けば、安定して高速(変動わずか0.016s)
- 小規模ファイル: 従来のtscより52%高速(1.160s → 0.553s)
(大規模プロジェクトでは最大10倍高速化の実績(VS Codeで10.4倍、Playwrightで10.1倍)みたいです)
引用元: https://devblogs.microsoft.com/typescript/typescript-native-port/
「最適な言語」を選ぶには、以下のような観点から選択すると良いと思います。
- プロジェクトの性質に応じた選択
- チームのスキルとの整合性
- 長期的な保守性の考慮
- パフォーマンス要件の明確化
型安全性は実行速度を犠牲にすることもありますが、ソフトウェア開発全体の生産性と品質を大幅に向上させる投資として価値があります。まるで建築において、基礎工事に時間をかけることで建物全体の安全性と耐久性を確保するのと同じように、型安全性は長期的な開発プロジェクトの成功を支える重要な基盤なのです。
そして今回検証したTypeScript 7.0プレビュー版は、この「型安全性 vs 開発効率」のトレードオフに新たな可能性を示しています。コンパイル速度の大幅な改善(52%高速化)により、型安全性を保ちながらもより快適な開発体験を実現する未来が見えてきました。