From 75093d767a8209b6b58110fd8703c6c642be2042 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sun, 27 Nov 2022 00:45:12 +0100 Subject: [PATCH 1/4] add benchmark --- Cargo.toml | 5 ++ benches/kakasi_benchmark.rs | 12 +++ tests/rust_article.txt | 155 ++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 benches/kakasi_benchmark.rs create mode 100644 tests/rust_article.txt diff --git a/Cargo.toml b/Cargo.toml index badc3cb..2960589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,13 @@ phf_shared = "0.11.1" unicode-normalization = "0.1.22" [dev-dependencies] +criterion = "0.4.0" rstest = { version = "0.15.0", default-features = false } +[[bench]] +name = "kakasi_benchmark" +harness = false + [profile.release] # These settings optimize for size codegen-units = 1 diff --git a/benches/kakasi_benchmark.rs b/benches/kakasi_benchmark.rs new file mode 100644 index 0000000..cebfc74 --- /dev/null +++ b/benches/kakasi_benchmark.rs @@ -0,0 +1,12 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +const RUST_ARTICLE: &str = include_str!("../tests/rust_article.txt"); + +fn rust_article(c: &mut Criterion) { + c.bench_function("rust_article", |b| b.iter(|| { + kakasi::convert(black_box(RUST_ARTICLE)) + })); +} + +criterion_group!(benches, rust_article); +criterion_main!(benches); diff --git a/tests/rust_article.txt b/tests/rust_article.txt new file mode 100644 index 0000000..396b45e --- /dev/null +++ b/tests/rust_article.txt @@ -0,0 +1,155 @@ +Rust(ラスト)は、性能、メモリ安全性、安全な並行性を目指して設計されたマルチパラダイムのプログラミング言語である。C言語、C++に代わるシステムプログラミング言語を目指しており[2]、構文的にはC++に似ているが[3]、「ボローチェッカー」(borrow checker) で参照の有効性を検証することによってメモリ安全性を保証できる。Rustはガベージコレクションなしでのメモリ安全性を達成しており、必要な場面で参照カウントを使うこともできる[4][5] 。 + +Rustプロジェクトはオープンソースのコミュニティベース開発で進行しており[6]、言語仕様(検討段階含む)、ソースコード、ドキュメントはオープンソースライセンスで公開されている[7]。2006年の開発初期は、Mozillaの従業員のグレイドン・ホアレ(Graydon Hoare)[8]の個人プロジェクトだったが、2009年にMozillaが開発に関わり始めてMozilla Researchの公式プロジェクトとなった[9][10]。2015年に1.0版がリリースされるまでにいくつもの破壊的な仕様変更があったが、1.0版以降は基本的には後方互換を保って6週間間隔で定期的にリリースされている。 + +Rustはマルチパラダイムプログラミング言語であり、手続き型プログラミング、オブジェクト指向プログラミング、関数型プログラミングなどの実装手法をサポートしている。基本的な制御構文はC言語に似ているが、その多くが式(expression)であるという点においてはML言語に似ている。コンパイル基盤にMIRとLLVMを用いており[11]、実行時速度性能はC言語と同等程度である[12]。強力な型システムとリソース管理の仕組みにより、メモリ安全性が保証されている。 + +Rustは2016–2022年の間Stack Overflow Developer Surveyで「最も愛されているプログラミング言語」で一位を獲得し続けている[13]。一方で、Rustは学習難易度が高い言語とも考えられており[14]、2017年ロードマップでは学習曲線の改善を目的として挙げていた[15]。 + +Rustという名前はさび菌に因んで付けられた[16]。この命名の後、当時の開発チームメンバーの多くが自転車を愛好していたことから、自転車のチェーンリングの錆つきを元にしたデザインのロゴが考案された[17]。当然、チェーンリングの錆がさび菌によって生じるわけがなく、「設計者のグレイドン・ホアレが生物学オタクであることから酸化鉄の「錆」ではなく「さび菌」なのだ」と、参考文献16の末尾に記されている。 + +開発体系 +Rustはオープンソースのコミュニティベースで開発が進められている。プロジェクトの主管はRust Project Developers(Rust開発チーム)である[18]。2018年現在、言語発明者のグレイドン・ホアレはプロジェクトチームから外れている。プロジェクトの全てのソースコードはGitHubで一般に公開されており[19]、コミュニティメンバーの協力により改良が進められる。プロジェクトの大部分のコミットはコミュニティメンバーによるものである[20]。 + +Mozillaの理念[21]を守り、Rust言語は社会に開かれており、言語やツールに対する仕様の提案はRFCの形で取りまとめられ[22]、関連するチケットで仕様詳細について利用者から感想や提言を受けている[23][24]。 + +言語やコアライブラリを開発するRust開発チームはServo(ウェブブラウザ用レイアウトエンジン)やRustコンパイラの実装経験を通して言語を改良している。例えば、C言語とRustを連携させるコードを自動生成するbindgenは元々は外部開発者が開発していたが[25]、Mozilla Servo開発チームがServoの開発で利用するためにC++とRustを連携させるコードも自動生成できるよう改良を重ね、現在はServo開発チームと密に連携しているRust開発チームに主管を移行して[26]Servoの開発と共に改良が続けられている[27]。 + +設計と特徴 +パラダイム +言語実装手法においてのプログラミングパラダイムは特定の実装手法に限定されない非純粋なマルチパラダイムプログラミング言語である。文法の表面的な記述は手続き型プログラミング、ブロックコードのまとまりの意味論は関数型プログラミング、型システムを用いたオブジェクト構造はオブジェクト指向プログラミングのパラダイム特性を持つ。特定のプログラミングパラダイムのみを使用してソースコードを記述することはなく、要所々々の必要な所で必要なパラダイムを使用してソースコードを記述することになる。 + +ソースコードをターゲットプラットフォームに最適化したオブジェクトファイルにコンパイルするコンパイル言語のパラダイム特性を持つ。多くのスクリプト言語が持つインタプリタ機能や、SwiftやGoが提供するようなREPL機能のパラダイム特性は持たない。 + +その他のパラダイムとして、関数の入出力パラメータにジェネリック型を指定するジェネリックプログラミング、非同期機能を実現する並行計算のパラダイム特性を持つ。 + +基礎文法 +Rustの基礎的な文法はC言語やC++に似て、波括弧で囲まれたブロックコード{ ... }、if, else, whileなどの制御フローキーワードを持つ。 全てのC言語やC++のキーワードが実装されているわけではなく、一方で幾つかのRustの制御命令(パターンマッチのためのmatch)はそれらの言語を習得したプログラマにとっては馴染みが少ないものも存在する。Rustの命令文は、表面的なC言語やC++との文法の類似性にもかかわらず、意味論的にはML系言語の命令式に近く、関数本体のほぼ全ての部分は制御フロー演算子でさえ「文(statement)」ではなく「式(expression)」である[28]。例えば普通のif式もC言語で言う所の条件演算子であり、if式の結果として返り値を返す。 + +ブロックコード内の命令文のセパレータにはセミコロン(;)を用いるが、C言語のそれと異なりRustのセミコロンは直前の命令文がブロックコードで括られる式の途中式であることを宣言するためのものである。セミコロンを末尾に置かない命令文はブロックコードの最終的な評価式として扱われ、その式の結果がブロックコードの外へ戻り値として返される[29]。これは関数定義であれば、結果を返す箇所にはセミコロンをつけない。結果が不要ならばセミコロンによって明示的に捨てるのである。そして結果を返さないならば、それは文と呼ばれる。 + +変数 +変数はletキーワードで定義され、不変(イミュータブル)である[30]。変数への再代入はコンパイルエラーとなる[31]。 + +可変変数を扱いたい場合、変数宣言時にmutキーワードを利用して可変であることを明示しなければならない[32]。この機能は例えば巨大構造体のコピーを避けながら一部を変更するために有効活用できる。 + +またRustはシャドーイング(同名変数の複数回定義)に対応している[33]。シャドーイングにより一時的な変数の変更や変数型の変更が可能になる[34]。 + +変数とは別にconstキーワードで定義される定数を持つ。定数は常に等しい値を持つ(mutやシャドーイングに対応していない)。 +型とポリモーフィズム + +この言語の型システムではHaskell言語に倣い「型クラス」を用いることができる。これはアドホックな多相性を容易にするものであり、可変型宣言により実現されるものである。高カインド多相性[35]など、Haskell言語にある他の特徴はサポートされていない。 + +型システム +Rust言語が備える型システムは、impl(実装)、trait(トレイト)、struct(構造体)およびenum(列挙型)を基本として構成される。implが他の言語におけるクラスに近い役割を果たし、継承とポリモーフィズムはtraitによって提供される。traitにはメソッドを定義することができ、traitで宣言されたメソッドはimplによってstructへミックスインされる。structにはフィールドが定義可能で、traitとimplはそれ自身にはフィールドは定義できない。enumには複数種類の型のカテゴリ変数が定義可能で、数値型、文字列型、オブジェクト型などの複数の状態を選択的に持ちうる。菱形継承問題を回避するためにtraitのみが継承が可能である。 + +変数の型を決定する型システムは静的型付けかつ強い型付けである。静的型付けと動的型付けの区分においての型付けは、コンパイル時に全ての変数に対して型を決定する静的型付けを基本としているが、トレイトをポインタを介して利用することで、ダックタイピングに似た型付けが可能なトレイトオブジェクトが存在する。ほかに、Anyトレイトにて、実行時リフレクションを用いた動的型付けも可能である。強い型付けと弱い型付けの区分においての型付けは、実行時に変数の型を型変換(アップキャスト、ダウンキャスト、クロスキャスト)およびボクシングをすることを許さない強い型付けのみをサポートしている。C言語、Javaは実行時に型変換をすることを許す弱い型付けをサポートしているが、Rust言語ではそのような型変換は認められていない。 + +型推論 +Rustコンパイラは変数への代入時(variable = value)、変数の型を値の型に基づき型推論する。変数の宣言には必ずしも型を決定するための初期値を必要としない。変数の宣言時に初期値が与えられた場合は「変数の型」は「初期値の型」であると型推論がなされるが、初期値が与えられなかった場合は以降のブロックコード中のその変数へ値が初めて代入された時に「左辺の変数の型」は「右辺の代入する値の型」であると型推論がなされる。変数への代入が型不一致により失敗した場合にはコンパイル時にエラーを検出する[36]。 + +ポリモーフィズムの実現 +ポリモーフィズムを実現するため、構造体のフィールドおよび関数の入出力値は特定のトレイトの実装をするジェネリック型を指定することが出来る。そのような定義の中では、ジェネリック型で型が宣言された変数および入出力値はそのトレイトの特性のみ使用できる。これはジェネリック関数が定義されるとすぐに型判定ができることを意味している。これはC++のダック・タイピングで具体的な型がインスタンス化されるまで判定できないテンプレートとは対照的である。しかし、Rustのジェネリクスの実装はC++のテンプレートの典型的な実装と似ており、インスタンス化ごとにコードの個別のコピーが生成される。これは単相化[37]と呼ばれ、JavaやHaskellで通常使用される型消去方式とは対照的である。単相化の利点は特定のユースケースごとに最適化されたコードであり、欠点は結果のバイナリのコンパイル時間およびサイズが増加することである。Rustのトレイトを用いたポリモーフィズムの実現は、実行時のオーバーヘッドがない「ゼロコスト抽象化」と表現されている[38]。 + +リソース管理 +Rustはメモリやその他リソースの管理にRAIIを採用しており、リソースの利用区間をコンパイル時に静的に検証するボローチェッカー(borrow checker)という機能で実現されている。ガベージコレクションや参照カウントとは異なり、コンパイル時に静的にリソースの利用区間を検証することで、実行時のリソース管理コストを少量に抑えている。 + +ボローチェッカー +一つのリソースは一つの所有者(変数やブロック)のみに関連付けされるという制約がRust特有のリソース管理機能として存在する。これは、あるリソースの所有権(ownership)はある所有者(owner)が持っている、のように表現される。同時に、リソースの所有権を複数の変数が持つことは出来ない。これはC++のスマートポインタの一種であるunique_ptrの振る舞いに似ている。ボローチェッカーは所有権の競合および不正利用を検証する。 + +リソースには参照(&記号)の概念が存在し、これは所有権の概念と並行して扱われる。所有権は一つの所有者しか持てないが、参照は複数の所有者が持ちうる。参照は同一のリソースを複数の箇所で利用するために利用される。ただし、所有権によるリソース管理は有効であり、所有権が解放されたリソースを指す参照は同時に無効化される。ボローチェッカーはこの参照についてもライフタイム(lifetimes)としてリソースの生存期間を検証する。 + +ボローチェッカーはリソースの所有権とライフタイムの両方を静的に解析することで、ヌルポインタや不正なメモリ域を指すポインター(英語版)、リソース利用競合を制限したメモリ安全性を実現している。 + +特異なリソース型 +リソースのメモリ確保は基本的にはスタックメモリを利用しており、ヒープメモリを利用したメモリ確保はBoxやVecなどの特定の型のみで限定的に利用されている。 + +コンパイル時に型のサイズが決定できない可変長配列を表すような型はヒープメモリを利用するBox型を使用してリソースを管理する。Boxリソース自体はスタックメモリで管理されるが、Boxリソースが所有権を持つ実体のリソースはヒープメモリで管理される。標準のヒープメモリ確保にはシステムアロケータを利用するが[39]、対象プラットフォームやビルド時の指定により他のメモリ確保APIを利用することもある。ヒープメモリはスタックメモリに比べて速度性能が落ちるため、必要時にのみ限定的に利用されている。 + +境界チェック +Rustは実行時に境界チェックをおこなう。これによりバッファオーバーランをはじめとしたメモリアクセスに対する安全性を得ている。この機能はゼロコスト抽象化でなく実行時の命令であるため、安全性と引き換えにいくらかの性能オーバーヘッドが発生している。他のシステムプログラミング言語として代表的なC言語やC++は境界チェックを強制しないため、Rustの特徴の1つとなっている。 + +ライブラリ +Rustのライブラリはクレート(crate)という呼称で提供される。多くのクレートはcrates.ioで公開されている。同サイトはバージョン毎のソースコードをアーカイブとして提供している。クレートは必ずしもcrates.ioに登録されている必要はなく、Webサーバやファイルシステムを指すURIで指定することもできる[40]。ライブラリはソースコードプロジェクトで配布されることが一般的だが、コンパイルしたバイナリライブラリファイル(拡張子rlib)で出力することも可能である。 + +コアライブラリ +コアライブラリはcoreの名称で提供されている[41]。このライブラリは標準ライブラリに依存しない基幹ライブラリであり、一切の上位ライブラリ、システムライブラリ、libcライブラリにリンクしていない。コアライブラリは最小限であり、ヒープ割り当てを認識せず並行性や入出力も提供していない。それらのことはプラットフォームへの調整が必要であり、このライブラリはプラットフォームに依存しない。 + +標準ライブラリ +標準ライブラリはstdの名称で提供されている[42]。このライブラリは基礎的な型(VecやOption)、言語の基本の処理、標準のマクロ、入出力(I/O)、マルチスレッドなどの機能を提供する。標準ライブラリは標準でリンクしてソフトウェアがビルドされるが、より根幹的なソフトウェアやリソースの小さい組み込み環境ではリンクを外してソフトウェアをビルドすることができる。 + +外部ライブラリ +Rustは基本的で汎用的な機能を含め標準ライブラリではなく外部ライブラリとして提供している。これはいわゆる「バッテリー同梱 (“Battery Included”)」の反対を行くもので、言語と共に更新することで保守的になりがちな標準ライブラリではなく、言語とは独立して更新することで最善を繰り返し探求しうる外部ライブラリとすることで、それらの機能をより良い品質で提供する考えに基づいていたものである[43]。外部ライブラリの利便性と品質の保証のためにcrates.ioとAPIガイドラインを提供している。 + +言語開発のコミュニティがそれらについて携わっていないわけではなく、基礎的な外部ライブラリはコミュニティ配下で開発が進められている。 + + 基礎的なライブラリ + rand - 乱数生成[44] + regex - 正規表現[45] + chrono - 時刻操作[46] + libc - C言語インターフェース[47] + log - ログ出力インターフェース[48] + +開発ツール +Rustの開発ツールは独立したソフトウェアとして提供されているが、Rustの公式なインストール方法に従えば、以下のツール一式が手に入る。 + +rustc +rustcはRustで記述されたRustソースコードをコンパイルするコンパイラ(処理系)である。コンパイルの成果物として中間コード、および実行ファイル、静的ライブラリ、動的ライブラリを出力する[49]。rustcはクロスコンパイルを可能とし、Windows、Linux、macOS向けの実行ファイル他、AndroidやiOSで動作するライブラリをホストマシンで出力することができる[50]。 + +対象プラットフォームは完成度に合わせて3つのティアに分かれている[51]。ティア1はバイナリリリースが提供されており、自動ビルド(英語版)と自動テストが整備され、それらが安定して成功(パス)することが保証されている。 ティア2はバイナリリリースが提供されており、自動ビルドと自動テストは整備されているがテストが成功することは保証されていない。ティア3はソースコードとしては対応しているが、自動ビルドと自動テストの動作が保証されておらず、公式リリースは提供されていない。 + +Windows、Linux、macOS向けのRustコンパイラはティア1でリリースされている。Android、iOS、WebAssembly向けのRustコンパイラはティア2でリリースされている。 + +Rust 1.12版より導入されたMIR (Mid-level IR)[11] によって、コンパイルと実行時間の迅速化ならびに型チェックの正確性の実現が図られている。 + +Cargo +CargoはRust製ソフトウェアプロジェクトのCUIのビルドツールである。規定のファイル構成(ディレクトリ、設定ファイル、ソースファイル)のプロジェクトディレクトリで利用される。プロジェクトのビルド(コンパイル)、依存ライブラリのダウンロード、テスト、ドキュメント生成などをサポートする[53]。通常はCargoを中心に開発を行えるように設計されているため、rustcコンパイラを直接呼び出すことは稀である。Cargoの依存ライブラリのダウンロード先はcrates.ioである[54]。 サブコマンドは拡張可能で標準のコマンドの他、README.mdファイルの自動生成コマンド[55]などの拡張コマンドが存在する。この拡張はcargo-xxxと名付けたコマンドを適切にインストールするだけで、cargoにxxxというサブコマンドを追加できる。 + +rustup +rustupはツールチェーンの管理ソフトウェアである。ツールチェーンのダウンロードとインストール、ソフトウェアバージョンの管理、コンパイルターゲットの切り替えの機能を提供する[56][57]。 + +機能 +rustupはRustプロジェクトがリリースしているコンパイラ(rustc)、ビルドツール(Cargo)などのツールチェーンをインターネットを経由してホストマシンにダウンロード、インストールおよびマネージメントする機能を提供する。インストール対象のツールチェーンのバージョンは、安定版、ベータ版、ナイトリー版の全てを含んでおり、利用者が必要とするバージョンを指定してダウンロードすることができる。また、ターゲットプラットフォームについても全ての種類を含んでおり、ホストマシンと異なるプラットフォーム(例えば、ホストがmacOSであってもWindowsやAndroid、iOS)のコンパイラをインストールすることができる。この場合、コンパイラはクロスコンパイラとして動作してターゲットプラットフォームに合わせたビルドを実施する。 + +歴史 +Rust 1.13版以前はシェルスクリプトで実装されたrustup.shが公開されていた[58]。これと並行してRust言語で実装された同等機能を提供するrustup.rsの開発が進められており、Rust 1.14版でrustup.shは非推奨となり[59]、Rust 1.14版以降はrustup.rsが公式リリースとして提供されている[60][61]。 + +評価 +Rustは2016〜2022年に渡ってStack Overflow Developer Surveyで「最も愛されているプログラミング言語」で一位を獲得し続け[13]、プログラマの好意的な評価は多い。 + +一方で、Rustは学習難易度が高い言語とも考えられている[62]。多くのRust初学者のプログラマにおいて、自身が正しいと思って書いたソースコードのコンパイルに対してコンパイラ(ボローチェッカー)がエラーを検出する、「ボローチェッカーとの戦い」(fighting with the borrow checker)が発生しがちである[63][64]。小さなアプリケーションの実装について同時期に言語開発されているGo言語と比較した場合、Rust言語よりGo言語の方が開発効率が良いという評価がある[14]。学習難易度の高さは開発チームも認識しており、2017年ロードマップでは学習曲線の改善を目的として挙げていた[15][65]。 + +この問題を改善するために、2020年現在において、自動化をメインテーマにした開発を続けており、前述にあるcargo、rustup等のツール以外にも、rustfmt、clippy、cargo docなどのツール類をIDE環境のみならずCUI環境でも利用可能な状態へと整備を続けている。また、後方互換性を維持しているため、crates.ioを用いる事でライブラリ類などの生産性の向上にも努めている。 + +Rustの実行時速度性能は同じくコンパイラにLLVMを利用している場合のC言語と同等程度であり[66][67]、一部の処理ではC言語を上回った速度が確認されている[12]。2018年2月時点で、ウェブサーバアプリケーションの汎用処理では速度性能の良いRust製ライブラリは開発が進んでいない[68]。単純なテキスト処理では速度性能は良い[69]。 + +その後のベンチマークでは、並行処理やグラフィックス処理においても、JavaやGoを上回っており、コンパイラの出力するコードの優秀性が証明されている。ただし、サポートされていないグラフィックスボード(Cのライブラリをインクルードしなければならない)では、C/C++の実効性能には及んでいない。 + +誕生 +2006年、Mozillaで働いていたグレイドン・ホアレ[70]は現代のインターネット環境で高い並行性と高い安全性が求められるシステムプログラミング言語としてC言語やC++に代わりうるプログラミング言語 Rust言語の開発に着手した[2]。MozillaがRustの開発に関わりはじめたのは2009年で[71][9]、Rustは2010年のモジラ・サミットで公に姿を表した[72]。RustソースコードをコンパイルするRustコンパイラは、初期の頃はOCaml言語で作られたコンパイラ(rustboot)が用いられていたが[73]、2010年にはRust言語自身でRustコンパイラを作るセルフホスティングへ移行したコンパイラ(rustc)の開発が始められ[74]、翌年にはブートストラップ問題を解決した最初の完動品が完成した[75]。この時よりRustコンパイラはコンパイル基盤にLLVMを用いたものとなった[76]。 + +0.x版 +プレアルファ(0.1版)と呼ばれる最初のバージョンは2012年1月にリリースされた[77]。Mozillaはこれを新しいモノ好みの人やプログラミング言語愛好家のためのアルファ版と位置づけていた。最初の安定版である1.0版がリリースされるまでの0.x版リリースでは、いくつもの大きな破壊的な仕様変更が言語およびライブラリに加えられた。 + +変数名や関数名など識別子は、この言語の開発の初期の頃は、ASCII文字以外の文字を使うことが禁じられていた。言語についてのある質疑応答の中で、現場の非英語圏のプログラマーのほとんどが識別子にはASCII文字を使っていると述べられていた。しかしその制限は反論を引き出すことになった[78]。それにより、2011年2月に言語に変更が行われ、この制限は削除された[79]。 + +型判定は、0.4版以前の従来の静的型付けに加えて、型状態システムをサポートしていた。型状態システムは、特別なcheck文を使用してプログラム文の前後での型決定をモデル化した。C言語やC++コードでのアサーションの場合と同様に、プログラム実行中ではなくコンパイル時に型不一致を検出する。型状態の概念はRust特有のものではなく、NILで採用されていたものである[80]。実際にはほとんど使用されていなかったため型状態は削除されたが、ブランディングパターンで同じ機能を実現できる[81]。 + +オブジェクト指向のシステムは、0.2版から0.4版にかけて大きく変わっていった。0.2版で初めてクラス(class)が導入され、0.3版でインターフェース(interface)を通したデストラクタとポリモーフィズムが追加され、0.4版で継承を提供する手段としてトレイト(trait)が追加された。インターフェースはトレイトに一部の機能が統合されて、異なる不要な機能として削除された。クラスもまた削除され、構造体とトレイトを使用したミックスインに置き換えられた。 + +コアのメモリモデルは、0.9版から0.11版の間、2つのビルトインのポインタ型(ヒープメモリ型~とガベージコレクション型@)を機能を単純化するために用意していた。それらは、標準ライブラリのBox型とGc型で置き換えられ、Gc型は最終的には削除された[82]。 + +2014年1月、Dr. Dobb's Journalの編集長アンドリュー・ビンストックはD言語やGo言語、Nim言語と同様にC++の競争相手になる機会を得たとコメントした。ビンストックによると、Rustは非常にエレガントな言語として広く見られているが、バージョン間で繰り返し大きく変更されたため普及が遅れているという[83]。 +1.x版 + +2015年5月16日、安定版となるRust 1.0がリリースされた[84]。1.0版の後、安定版およびベータ版が6週間おきに定期リリースされている[85]。またナイトリー版が毎日リリースされている[86]。新たな機能はナイトリー版で開発が進められ、ベータ版で動作が検証されている[87]。 + +2016年8月2日にリリースされたFirefox 48にはRustで書き直されたメディアパーサが組み込まれており、Mozillaが開発しているブラウザのFirefoxにおいてRustで書かれたコードが書き加えられる初例となった[88][89]。 + +2016年9月にRustはFedora 24の標準コンポーネントに加えられ、RPMのパッケージ管理システムを用いてのインストール作業が容易化されている[90]。 + +2018年版 +2018年12月6日にバージョン1.31がリリースされた[91]。今回からエディション制が導入され、最新版は"Rust 2018"、従来版は"Rust 2015"と呼ばれることになる。言語機能への破壊的変更は新しいエディションのみに適用されるが、互換性に影響しない機能追加は引き続き両者に適用される。また、2015年版から2018年版へはcargo fixでトランスコンパイルできる[91]。 + +2021年版 +2021年5月11日に、3年ぶりの改訂となる2021年版について公式 Blogへ記載が行われた[92]。当該機能は、2021年10月21日にリリースの1.56より適用される。今回の主要な改訂は、例外処理への対応が強化され、prelude文にTryIntoやTryFrom、FromIteratorなどが追加される。詳細については、公式Blogを参照されたいが、後方互換性を維持しているため、2018年版との間では特に問題なくコンパイルできる。 From 3d42d116a8fc46c37b9c7b3699e1819f339a5d7a Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sun, 27 Nov 2022 15:43:59 +0100 Subject: [PATCH 2/4] add profiling, small performance improvements --- Cargo.toml | 3 +- benches/kakasi_benchmark.rs | 20 +++++- benches/perf.rs | 75 ++++++++++++++++++++++ codegen/src/main.rs | 11 +++- src/lib.rs | 121 ++++++------------------------------ src/types.rs | 45 +++++++++----- tests/tests.rs | 94 ++++++++++++++++++++++++++++ 7 files changed, 246 insertions(+), 123 deletions(-) create mode 100644 benches/perf.rs create mode 100644 tests/tests.rs diff --git a/Cargo.toml b/Cargo.toml index 2960589..5e813ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ unicode-normalization = "0.1.22" [dev-dependencies] criterion = "0.4.0" +pprof = { version = "0.11.0", features = ["flamegraph"] } rstest = { version = "0.15.0", default-features = false } [[bench]] @@ -26,7 +27,5 @@ name = "kakasi_benchmark" harness = false [profile.release] -# These settings optimize for size codegen-units = 1 -opt-level = 'z' lto = true diff --git a/benches/kakasi_benchmark.rs b/benches/kakasi_benchmark.rs index cebfc74..1506161 100644 --- a/benches/kakasi_benchmark.rs +++ b/benches/kakasi_benchmark.rs @@ -1,12 +1,26 @@ +mod perf; + use criterion::{black_box, criterion_group, criterion_main, Criterion}; const RUST_ARTICLE: &str = include_str!("../tests/rust_article.txt"); -fn rust_article(c: &mut Criterion) { +fn benchmark(c: &mut Criterion) { + c.bench_function("short", |b| b.iter(|| { + convert("安定版となるRust 1.0がリリースされた[84]。1.0版の後、安定版およびベータ版が6週間おきに定期リリースされている[85]。") + })); + c.bench_function("rust_article", |b| b.iter(|| { - kakasi::convert(black_box(RUST_ARTICLE)) + convert(RUST_ARTICLE) })); } -criterion_group!(benches, rust_article); +fn convert(text: &str) { + kakasi::convert(black_box(text)); +} + +criterion_group!{ + name = benches; + config = Criterion::default().with_profiler(perf::FlamegraphProfiler::new(100)); + targets = benchmark +} criterion_main!(benches); diff --git a/benches/perf.rs b/benches/perf.rs new file mode 100644 index 0000000..9f0ab23 --- /dev/null +++ b/benches/perf.rs @@ -0,0 +1,75 @@ +// Source: https://www.jibbow.com/posts/criterion-flamegraphs/ + +use std::{fs::File, os::raw::c_int, path::Path}; + +use criterion::profiler::Profiler; +use pprof::ProfilerGuard; + + +/// Small custom profiler that can be used with Criterion to create a flamegraph for benchmarks. +/// Also see [the Criterion documentation on this][custom-profiler]. +/// +/// ## Example on how to enable the custom profiler: +/// +/// ``` +/// mod perf; +/// use perf::FlamegraphProfiler; +/// +/// fn fibonacci_profiled(criterion: &mut Criterion) { +/// // Use the criterion struct as normal here. +/// } +/// +/// fn custom() -> Criterion { +/// Criterion::default().with_profiler(FlamegraphProfiler::new()) +/// } +/// +/// criterion_group! { +/// name = benches; +/// config = custom(); +/// targets = fibonacci_profiled +/// } +/// ``` +/// +/// The neat thing about this is that it will sample _only_ the benchmark, and not other stuff like +/// the setup process. +/// +/// Further, it will only kick in if `--profile-time