データ・サイエンス言語としてのPerl5
シリーズ紹介 —Post 0 of N
この投稿は、Perl5 + PDLで完全に記述されたベクトルデータベースエンジン(VDBE)の共同開発を文書化したシリーズの最初の投稿です。後の投稿は、そのエンジンのすべてのコンポーネントを通過します。これはステージを設定します。このシリーズの主な推進力は、パフォーマンス請求を行わないので、VDBEをダンプさせることではなく、他の言語で達成できるほぼすべてのことを達成するためにPerlをどのように使用するかを示すことですが、よりスマートです!
1. データ・サイエンスにPerl5を使用する理由
データ・サイエンティストが言語の選択について話し合うと、会話はPython、RまたはJuliaにすばやく収束します。Perl5は、テーブルに座ることはめったにありませんが、2番目の外観に値する魅力的な一連の特性を持ちます。これらの特性は、長年にわたって実質的に変化していません(Perl5は常にこのようになりました)。言語に触れられ、その地形、合理性、柔軟性、表現可能性を高く評価することを学び、実際にあなたの仕事を前進させるためにそれを使用していない限り、これらの機能はPerl5で無料になるだけでなく、プロジェクトを前進させるのに役立ちます.
ユビキティとゼロインストールの展開
Perl5は、ほぼすべてのUNIX系オペレーティング・システムのデフォルト・コンポーネントとして出荷されます。Linuxディストリビューション、macOS、BSD、および多くの組込みLinux環境はすべて動作しています。perl Binary Out Of The BoxシングルここでPythonが始まっていますが、Perlが存在し、完全なPythonスタックが存在しないヘッドレスサーバー、ネットワークアプライアンス、またはHPCログインノードを見つけることは一般的です。Perlに書き込まれたデータ・パイプラインは、conda 環境、A venvまたはコンテナ。
データ・センターからエッジへの移植性
256コアのHPCノード上のテラバイト・データセットを分析するのと同じスクリプトで、構成の変更が少ない場合は、Raspberry Pi、IoTゲートウェイまたは組込みコントローラで実行できます。Perlの単一バイナリ・デプロイメント・モデルと実行時のオーバーヘッドが低いため、”一度書けば、どこでも実行できる” PythonのインタプリタのオーバーヘッドまたはJuliaのJITウォームアップ時間が許容できない環境での言語。
どこにでもデプロイする場合は、everywhere Perl5を選択します。
テキストとデータ管理に基づく遺産
Perlは、テキスト処理、正規表現、および”アドヒーシブ” システム・コンポーネント間の作業。実際、科学的データ・パイプラインは数値計算ではなく、異種ファイル形式の読取り、乱雑なレコードのクリーニング、異なるソースからデータセットを結合し、結果をダウンストリーム消費コンポーネントにルーティングするというデータ・ラングリングによって支配されています。
Perlの正規表現エンジンは最も強力なものの1つであり、1つのライナーは、他の言語のヘルパー・ライブラリを必要とするデータ・クレンジング・タスクを実行できます。
あなたが科学コンピューティングの領域にいるならば、あなたはワークフロー管理システムと再現可能な研究の概念に遭遇したかもしれません。どちらも、エンドツーエンドのデータ変換とワークフローの実行に依存して、アナリストや科学者がそれぞれデータをインサイトや推論に変えるために行う必要のある、手作業でエラーが発生しやすい、面倒なポイントやクリック・アクティビティを排除します。
この勇敢な新しい世界では、Perl5の豊富な履歴により、ワークフローのコンポーネントとして、またはこれらのワークフローを実装するアプリケーション言語として、両方を輝かせることができます。
CPAN: テスト済みのモジュール・エコシステム
Comprehensive Perl Archive Network(CPAN)は、想像できるすべてのドメインで200,000を超えるモジュールをホストします。データ・サイエンスの提供はPythonほど広範囲ではありませんが、専用ビルダーの基本的なコンポーネントは次のとおりです。
PDL (Perl Data Language)— 強く型付けされたN次元配列を持つベクトル化された数値計算(以下で詳しく説明します)。
PDL::Stats—PDL ndarrays上に構築された記述統計、回帰、クラスタリング(k-means、ミニバッチk-means)など。
AI::MXNet、AI::TensorFlow— ディープ・ラーニング・バインディング。
Statistics::Regression、Statistics::Descriptive—PDL依存性がないクラシック統計。
テキスト::CSV、スプレッドシート::XLSX、データ::MessagePack、シリアル - 高パフォーマンスのシリアライズおよびI/O。
DBI +多数のデータベース・ドライバ — すべての主要なRDBMSへのSQLアクセス。
MCE (多コア・エンジン)— 共有および分散メモリー・ワークロードの構造化並列化。
Inline::C、Inline::CPP—CまたはC++コードをPerlソース・ファイルに直接埋め込みます。コンパイラは、スクリプトが初めて実行されたときに透過的に起動されるため、完全なXSビルド・システムを使用せずに、パフォーマンス・クリティカルなカーネルをその他の純粋なPerlプログラムにドロップするのは簡単です。
FFI::Platypus— 任意の共有ライブラリの関数を呼び出す(
.so/.dylib/.dll) XSまたはCの接着コードの単一行を書かずにPerlから。Platypusは、Cと同等の型、構造体、コールバック、およびクロージャをすべてサポートしており、PerlをBLAS、LAPACK、HDF5またはその他のネイティブ・ライブラリにバインドする最新の方法です。
モダン・ペルはおじいちゃんのパールではない
以下の機能は、公式リリースノート(perl5360delta, perl5380delta, perl5400delta)と、安定ステータスに達したか、最初に導入されたリリースによって編成されます。データサイエンスおよび科学計算ワークロードに関連する機能のみが強調表示されます。
Perl 5.36—2022年5月
**
use v5.36**— 機能バンドルが自動的に有効になりましたuse warningsその上use strict。また、無効化されます。indirectmethod-callの構文とmultidimensionalハッシュキーシミュレーション。微妙なバグの2つの一般的なソースを排除します。名前付きサブルーチン署名 *(5.36以降は安定、5.20以降は実験的)*— 関数のパラメータは、オプションのデフォルトで名前で宣言されるようになりました。▲
//=および||=default-value演算子はさらに5.38の署名に追加され、undefまたは、それぞれ虚偽:
use v5.36;
sub clamp ($val, $lo = 0, $hi //= 1) {
$val < $lo ? $lo : $val > $hi ? $hi : $val;
}
isaclass-instance演算子 *(5.36以降安定、5.32で導入)*—$obj isa "ClassName"次の値よりクリーンなブール値を返しますref($obj) eq "ClassName".builtinmodule *(stable since 5.40; experiment since 5.36)*— 字句的に読み込み可能な関数がインタプリタに直接組み込まれています。安定した5.40バンドルには、特に次のものが含まれます。ceil,floor— 整数丸めなしuse POSIX.trim— 文字列から先頭/末尾の空白を削除します。indexed- 各要素をインデックスとペアにします。forループ(以下を参照)。true,false,is_bool— 型付きブールセンチネル; シリアライザがJSONを生成できるようになりましたtrue/falseというより1/0.weaken,unweaken,is_weak— メモリーリークのない双方向データ構造体を構築するための参照カウント制御。blessed,reftype,refaddr- 参照イントロスペクション。
安定したブール追跡 *(5.36)*— ブールとして作成されたスカラー(例:
!!1)は、代入を通じてブール性を保持するようになり、JSONおよびMessagePackへの信頼できる型認識シリアライズが可能になります。複数値
forloops *(stable since 5.40; experiment since 5.36)*手動でインデックス演算を行わずにペアまたはNタプルを反復する:
use v5.40;
use builtin 'indexed';
for my ($i, $val) (indexed @scores) { ... } # index and value
または複数の値を同時に取得します。
use v5.40;
for my ($val1, $val2, $val3) (@scores) { ... }
deferblocks *(experimental since 5.36)*— ブロックが通常または例外を問わず、無条件にクリーンアップ・コードを実行するスコープ終了ガード。デストラクタベースのスコープガード・オブジェクトの自然な置換であり、データ・パイプラインでのリソース管理のための重要なパターンです。
Perl 5.38—2023年7月
PERL_RAND_SEED環境変数 *(5.38)*— 実行前にこの変数を設定すると、randコール(明示なし)srand)は同じシーケンスを生成し、ソース・コードを変更せずに再現可能確率アルゴリズム(シミュレーション、ランダム・サンプリング、モンテカルロ・メソッド)を有効にします。class/field/methodsyntax *(experimental since 5.38)*— どちらも必要ない、専用の字句スコープのオブジェクトシステムblessなし@ISACPANモジュールもありません。データセット行、モデル・パラメータ、パイプライン・ステージなどの型付き値オブジェクトを定義する場合に役立ちます。
use feature 'class';
no warnings 'experimental::class';
class Vector2D {
field $x :param;
field $y :param;
method magnitude { sqrt($x**2 + $y**2) }
}
my $v = Vector2D->new(x => 3, y => 4);
say $v->magnitude; # 5
Perl 5.40—2024年6月
try/catch例外処理 *(5.40以降安定、5.34以降実験)finallyblock added in 5.36)*— 構造化例外処理がコア言語機能になりました。CPANモジュールは必要ありません。
use v5.40;
try {
my $result = load_and_process($file);
}
catch ($e) {
warn "Pipeline error: $e";
}
finally {
close_resources(); # runs whether or not an exception was thrown
}
(Try::Tiny / Feature::Compat::Try 5.34より古いperlsをターゲティングする場合にのみ必要です。)
複数値
forloops *(stable since 5.40)*— 上記の5.36エントリを参照; 彼らはこのリリースで実験から安定に卒業しました。builtin::infおよびbuiltin::nan*(5.40以降の実験値)*— 型付き浮動小数点無限大および非数値定数、消去9**9**9POSIXは数値コードでハッキングします。^^logical XOR operator *(5.40)*— 中優先論理演算子セットを完了します(&&,||,^^); ブールマスク操作に便利です。use v5.40組み込み関数をインポート— 機能バンドルを有効にする以外に、use v5.40対応するものもインポートします。builtinバージョンバンドル、すべての安定したbuiltin::個別の名前のない短縮名として使用できる関数use builtin文。
長年の機能(5.36以前)
sayおよびstate*(5.10以降)*—say次であるprint暗黙の復帰改行state包含サブ(軽量メモリプリミティブ)の呼出し間で永続する字句を宣言します。ファーストクラスの参照と閉鎖 - 匿名のサブ、閉鎖、参照構造は基本であり、Perl 5以来安定しています。
**
use constant**またはCPANReadonly指定された定数のモジュールReadonly深い不変性を、use constantできません。
結合perlbrew またはplenv バージョン管理とcarton 再現可能な依存性スナップショットの場合、最新のPerlプロジェクトは、一流のソフトウェア・エンジニアリング作業のように見えて感じます。
正直な制限
No case for Perl is complete without honesty about where it falls short:
可視化—Perlは同等ではありません
ggplot2またはmatplotlib。通常、プロットには、R、gnuplot、またはWebライブラリへの外部コールが必要です。この弱点が実際の強みになる場合があり、他のアクターを編成および拡張するアプリケーション言語としてPerl5を使用できます。コミュニティの勢い - データサイエンスコミュニティがPythonとRに集約されました。既製のチュートリアル、スタックオーバーフローの回答、および共著者の検索はより困難です。
オブジェクトの向き - Moose/Mooを使用しない場合、OOPモデルは冗長で、依存関係が追加されます。新しい
class機能はこれらの問題を解決するかもしれませんType safety at scale— コア言語の動的スカラーは、大規模で協調的な数値コードベースを推論しにくくします(次の項を参照)。
2.Perlデータ型システム — 長所とキャッシュ時代の限界
コアPerlタイプ
Perlの基本的なデータ・モデルは、次の3つの構成に基づいています。
「Construct」Sigil「What It Holds」
|———–|——-|—————|
| スカラー $ |単一の値: number、string、reference、またはundef |
アレイ @ |整数で索引付けされたスカラーの順序付きリスト|
ハッシュ % |文字列がキーとなるスカラー値の順序なしコレクション|
他のすべてのもの(オブジェクト、クローズ、複雑なデータ構造)は、これらの3つのプリミティブからreferencesを介して構築されます。\@array, \%hash, sub { ... }).
このモデルは非常に柔軟です。1つの配列で、整数、浮動小数点数、文字列およびネストされた参照を同時に保持できます。こうした柔軟性こそが、ペルが二十年にわたって支配的なシステム管理とウェブスクリプティング言語になったのである。
キャッシュ階層の問題
最新のCPUは、データがL1/L2/L3キャッシュを流れる場合にのみ、ピーク・スループットを実現します。† 大規模で連続するブロック- spatial localityと呼ばれるプロパティPerl配列はこれを提供しません。フードの下では、Perl配列は、ヒープ割当てスカラーへのポインタのC配列です(SV)構造。各スカラーには、参照カウント、型タグ、およびパディング(通常は64ビット構築のスカラーあたり24 - 56バイト)が格納されます。したがって、100万要素以上のPerl配列を反復するには、ヒープ全体に散在する100万ポインタ間接参照が必要であり、最新のSIMDパイプラインの速度優位性を完全に否定するキャッシュミス・パターンが生成されます。
A concrete consequence: 純粋なPerlで記述された2つの1 000要素ベクトルのドット積は、L1キャッシュに快適に収まる2つのフラットな4 000バイトのメモリー領域を占めるPDLフロートndarraysのペアに対する同等の操作より、およそ100–1000×低速です。
Rとの対比
Rは好奇心旺盛な中盤。Perlと同様に、動的で解釈された言語です。変数は型付けされていないコンテナで、関数はファーストクラスの値で、対話型REPLは主要な開発環境です。Rは、Perlの3つのコア・タイプに直接類似しています。
| Perlコンセプト | Rアナログ |
|---|---|
$scalar |
length-1原子ベクトルまたはスカラーインリスト |
@array |
list() |
%hash 名前付きlist() |
|
リファレンス(\@arr) |
Rは明示的な参照を使用しません。代わりにcopy-on-modifyセマンティクスを使用します |
ただし、Rのworkhorse型、つまり原子ベクトルには、対応する単純なPerlはありません。Rアトミック・ベクトルは、連続的で均質に型付けされたメモリー・ブロックであり、まさにCPUキャッシュが与えるレイアウトです。Rのすべての組込みスカラーは、実際にはlength-1原子ベクトルです。”ベアスカラー” 原子ベクトルの外
この設計の選択は、ユーザーが単一のループを記述したり特別なループを割り当てたりすることなく、RコードがBLASレベルのスループットで数百万倍ものベクトルに対して自然に動作することを意味します。”配列” オブジェクト。
Rの原子型は次のとおりです。
| Rアトミック型 | ストレージ | C相当 |
|---|---|---|
logical |
4バイト/要素 | int (´・ω・`) |
integer |
4バイト/要素 | int32_t |
double |
8バイト/要素 | double |
complex |
16バイト/要素 | _Complex double |
character |
CHARSXPへのポインタ | char * (インターネット) |
raw |
1バイト/要素 | uint8_t |
R also defines higher-level structures built on atomic vectors:
- matrix—a 2-D原子ベクトル
dim属性。 - アレイ—N-D原子ベクトル
dim属性。 - data.frame— 等長原子ベクトルの名前付きリスト。
Rの表形式データ。 - factor— 整数ベクトル
levels属性; 質的データをエンコードします。
The lesson: 統計およびデータ・サイエンス・アプリケーションで使用されるRのコンピューティング・パフォーマンスは、その連続した原子ベクトルから直接流れます。Perlのパフォーマンスへの同等のパスは拡張です(これはスタンドアロンでもある)。matlab 例えば、Perl Data Language PDL.
3.PDLの入力: 強い型指定のN次元配列
Perl Data Language (PDL) pdl.perl.org)は、Perlをndarrays (N次元配列)で拡張します。これは、一流のPerlオブジェクトのようにルック・アンド・フィールとなる、強く型付けされた連続したメモリー・バッファです。
use PDL;
# A 1-D float ndarray — 4 bytes × 5 elements in one contiguous block
my $v = float( 1.0, 2.0, 3.0, 4.0, 5.0 );
# A 128-dimensional random database of 1000 vectors — all in cache-friendly memory
my $db = random( 128, 1000 ); # double by default
# Dot product of every DB vector against a query — a single BLAS call
my $scores = $db x $query->transpose;
PDLプリミティブ型
PDL exposes the full palette of C numeric types as first-class constructors:
| PDLタイプ | バイト | Cタイプ | コンストラクタ |
|---|---|---|---|
byte |
1 | uint8_t |
byte(...) |
short |
2 | int16_t |
short(...) |
ushort |
2 | uint16_t |
ushort(...) |
long |
4 | int32_t |
long(...) |
indx 4か8かssize_t |
indx(...) |
||
longlong |
8 | int64_t |
longlong(...) |
float |
4 | float |
float(...) |
double |
8 | double |
double(...) |
cfloat |
8 | _Complex float |
cfloat(...) |
cdouble |
16 | _Complex double |
cdouble(...) |
スレッドとSIMD
PDLの最も特徴的な機能の1つは暗黙的なスレッドです: 操作は余分な次元で自動的にブロードキャストされ、ユーザー・コードの明示的なループがなくなり、内部ループが最適化されたCまたはFortranカーネルに委任されます。結合set_autopthread_targ(N)、PDLは自動的に独立したスライスを平行にしますN OSスレッド — ユーザーが1つも書かずにfork またはThread::Queue コール。
不正な値
PDLには、bad valuesという概念が組み込まれています。PDL::Bad(Rとほぼ同じ) NA。ndarrayは次のようにフラグ付けできます。”誤値認識”、およびPDL操作は、算術、統計、およびI/Oによって正しく悪さを伝播します。
4. タイプ比較: Perl、PDL、Rサイドバイサイド
次の表は、一般的に使用されるすべてのR型を最も近いPerlとPDLの対応型にマップし、3つの言語が互いに一致、異なる、または補完している箇所を強調しています。
| |
|—|—|—|—|
主なポイント
純粋な数値、均質データ(ベクトル、マトリックス、テンソル)の場合、PDL ndarraysおよびR原子ベクトルは機能的に同等であり、同等に効率的です。
異種表形式データ (混合型、文字列列、ファクタ)、Rの場合
data.frameより人間工学的です。Perlは通常、配列のハッシュまたは次のような専用モジュールを使用します。Data::FrameまたはPDL::IO::CSV.テキスト、不規則構造、システム接着の場合、Perlのネイティブ型はRとPythonの両方よりも優れています。
したがって、Perl+PDLの組み合わせは、Rが統計言語として提供するものと、Perlがシステム言語として提供するもののunionを、急激な学習曲線と、すぐに使用できるND率直に限られた統計ツールのコストで提供します。
ただし、Perl+PDL+Rの組合せ(後者がコンポーネントとして使用されている場合、またはインストゥルメンタル Perlより)
5. ロードマップ: このシリーズの残りは何をカバー
このシリーズでは、Perl5 + PDLでゼロから構築されたベクトル・データベース・エンジンの構築について説明します。ベクトル・データベースは、最新の検索拡張生成(RAG)パイプライン、セマンティック検索、および最も近い推奨システムを支えています。最初の原則から1つを実装することは、Perlのシステムプログラミングの強みとともにPDLの数値機能を実証するための優れた手段です。
これらの投稿とともに共同開発されるディレクトリには、次のコンポーネントが含まれています。各コンポーネントは、専用リポジトリ内のファイルを参照する1つ以上の専用投稿の対象となります。
Post 1— シリアライズとI/O: VectorIO モジュール
ファイル: VectorIO.pm
エンジンはベクトルをパックされたバイナリBLOBとして内部に格納しますMessagePack ペイロード。この記事の内容:
- クリーンなモジュールを設計する
Exporter- ベースのパブリックAPIuse v5.40. - システム境界でスキーマの正確性を強制する検証ヘルパー。
Post 2— ベクトル・データベースのシミュレート
ファイル: simulate_vectorDB.pl
データベースを検索する前に、データベースが必要です。この記事では、次の内容を示します。
- 再現可能なランダムフロートベクトルの生成
PDL::random. - 使用
GetOpt::Long人間工学的なCLIオプション解析用。 - 書き込みA
--seed- 複数の実行で同一のデータベースを生成する制御シミュレーション- ベンチマークに不可欠です。
Post 3 - ベンチマーク: timing_DB モジュール
ファイル: timing_DB.pm
パフォーマンス要求には測定が必要です。この記事では、次のことを紹介します。
- 再利用可能なパールベンチマーキングハーネス
Time::HiRes. - Perl/PDLとR実装間の公正な壁時計比較の方法論。
- 異なるワークロード・サイズのスループット(ベクトル/秒)とレイテンシ(ミリ秒/問合せ)の解釈。
Post 4—K-MeansクラスタリングPDL::Stats::Kmeans
ファイル: kmeans.pl
K-meansクラスタリングは、近似近傍検索に対する逆ファイル索引(IVF)アプローチのバックボーンです。この記事の内容:
『THE PDL::Stats::Kmeans インタフェースとその返品契約(centroid, cluster, n, R2, ss).
- 解釈
[obs × clusters]返されたメンバーシップ・マスクrun_kmeans. - Perl/PDL k-meansのセントロイドをRと比較する
kmeans()およびClusterR::MiniBatchKmeans()数値の正確性を検証します。
Post 5— ミニバッチK-Means: 大規模なデータセットへのスケーリング
ファイル: compare_kmeans_centroids.pl
完全なk-meansでは、反復ごとにメモリー内のすべてのデータが必要です。ミニバッチk-meansは、メモリと計算を大幅に削減するために、少量の重心精度を取引します。この記事では、次の点を調査します。
- PDLで真の再サンプリングされたミニバッチループを実装する。
- フルバッチバリアントとミニバッチバリアントの間の重心ドリフト。
- Rの並べて出力
MiniBatchKmeansからClusterRパッケージ。
Post 6 - Inverted File Index(IVF)検索
ファイル: compare_ivf_search.pl
セントロイドを手元に置いて、データベースを分割し、近傍近傍サブリニア検索を実行できます。この記事の内容:
- Building the inverted lists: 各データベース・ベクトルを最も近い重心にマップします。
『THEunpack_inverted_listsヘルパーVectorIO. - Querying: 最上位Kの最寄りのセントロイドを見つけ、それらのリストのみを検索します。
- プローブされたリストの数が異なるため、精度と速度のトレードオフが異なります。
Post 7 - Rに対する検証: 数値的正確性と言語間パイプライン
ファイル: compare_kmeans_centroids.R, compare_kmeans_centroids_pure.R, plot_centroid_coordinates.R
The final post in the foundation series closes the loop between Perl and R:
- PDLの結果をCSVにエクスポートし、独立した検証のためにRで読み取ります。
- ggplot2を使用して、両方の言語の重心座標を同時に視覚化します。
- ワークフロー・パターン”Perlでの計算、Rでの可視化” 両生態系の強みを活かしています。
**Next up—Post 1:**シリアライズとI/O
VectorIO.pm
† 最新のCPUには、プロセッサ・コアとメインRAMの間に位置するcaches (L1、L2、L3)と呼ばれる、高速なオンチップ・メモリーが複数レベルあります。L1は、最小(通常はコア当たり32–64KB)で最速(1–4クロック・サイクル・レイテンシ)です。L2は大きく(256KB–1MB)、わずかに遅くなります。L3は、より高いレイテンシでコア(4–64MB)間で共有されます。メインRAMは、60から100 nsのレイテンシ(L1より約200×遅い)でさらに離れています。
計算が予測可能な連続したパターンでメモリーに接触すると、ハードウェアprefetcherは、必要になる前に今後のデータをL1/L2にロードできるため、ピークに近いスループットを実現できます。分散ポインタチェーシング(ヒープ割り当てスカラーのPerl配列のトラバースなど)はプリフェッチを破り、各キャッシュミスがRAMから解決されるのを待機しながらCPUをストールします。