Perl5 作為資料科學語言
系列介紹 — 0 篇 N
這篇文章是一系列中第一篇以 Perl5 + PDL 撰寫的向量資料庫引擎 (VDBE) 共同開發的文章。後續的貼文會逐步引導該引擎的每個元件;這會設定階段。這個系列的主要動力不是因為我沒有做出任何效能索償而傾印您的 VDBE,而是展示如何利用 Perl 實現任何其他語言所能實現的任何事情,但更聰明!
目錄
1. 為什麼選擇 Perl5 for Data Science?
當資料科學家討論語言選擇時,對話會快速融合 Python、R 或 Julia。Perl5 很少在桌子上拿了一個座位,但它擁有一套令人注目的特徵,值得一看。這些特徵多年來沒有重大變化 (Perl5 一直都是這種方式!),但除非您已經接觸到這個語言,並學習如何欣賞他們的慈悲、理性、靈活性、表達性,並實際用它來推動您的工作。,您不僅知道 Perl5 的這些功能是免費的,還能協助您推動專案發展。
Ubiquity 和零安裝部署
Perl5 提供幾乎所有類似 UNIX 作業系統的預設元件 — Linux 發行軟體、macOS、BSD 和許多嵌入式 Linux 環境均包含工作輪廓 立即可用的二進位檔。Python 一直都在這裡製造,但還是常見於尋找無頭伺服器、網路設備或 HPC 登入節點
其中 Perl 存在,而且沒有完整的 Python 堆疊。以 Perl 撰寫的資料管線可以在第一天執行,不需要錐體 環境,a 靜脈,或容器。
從資料中心到邊緣的可攜性
分析 256 核心 HPC 節點上 TB 資料集的相同命令檔,若有少量組態變更,可以在 Raspberry Pi、IoT 閘道或內嵌控制器上執行。佩爾’s 單一二進位部署模型和低運行時間開銷,使其成為真正的”寫入一次,隨處執行” Python 環境中的語言’解譯器間接費用或 Julia’無法接受 S JIT 暖機時間。
如果您打算在任何地方部署,而 everywhere Perl5 是您的明顯選擇。
以文字和資料融合為基礎的傳統
Perl 從頭開始設計,用於文字處理、正規表示式,以及”膠” 在系統元件之間工作。實際上,科學資料管線並非由數值運算所主導,而是 *資料整理 *:讀取異質檔案格式、清理訊息記錄、結合不同來源的資料集,以及將結果路由至下游耗用元件。
佩爾’正規表示式引擎仍然是最強大的可用項目之一,而且單線可以完成需要其他語言的協助程式程式庫的資料清除工作。
如果您在科學計算領域,您可能發現 工作流程管理系統 和 可重現研究 的概念。他們都依賴端對端資料轉換和工作流程的執行,以消除手動、容易出錯和繁瑣的點按活動,分析師和科學家必須分別將資料轉化為洞察力和推論。
在這個勇敢的新世界,Perl5’豐富的歷史記錄可以將兩者視為工作流程的組成部分,或作為實行這些工作流程的應用程式語言。
CPAN:經戰測試的模組生態系統
全方位的 Perl Archive Network (CPAN) 可讓每個網域擁有超過 200,000 個模組。雖然資料科學產品不像 Python 一樣廣泛,但專用的建構工具的基本元件都在這裡:
PDL (Perl Data Language) — 具有強烈鍵入 N 維度陣列的向量化數值運算 (涵蓋在下方深度)。
PDL::Stats — 描述性統計資料,迴歸,叢集 (k-means,mini-batch k-means) 等,建置在 PDL ndarray 上。
AI::MXNet,AI::TensorFlow — 深度學習連結。
**統計資料::迴歸 , 統計資料::描述性 **— 沒有 PDL 相依性的傳統統計資料。
-** 文字::CSV, 試算表::XLSX, 資料::MessagePack, 穀物 **— 高效能序列化和 I/O。
DBI + 數十個資料庫驅動程式 - 對每個主要 RDBMS 的 SQL 存取權。
MCE (多核心引擎) — 共用記憶體和分散式記憶體工作負載的結構化平行程度。
內嵌::C,** 內嵌::CPP** — 將 C 或 C++ 程式碼直接內嵌在 Perl 來源檔案中;第一次執行命令檔時,會以通透的方式呼叫編譯器,讓它在沒有完整 XS 組建系統的情況下,將效能關鍵核心刪除。
FFI::Platypus — 任何共用程式庫中的呼叫函數 (
.so/.dylib/.dll) 從 Perl 而不撰寫單行 XS 或 C 膠條碼。Platypus 支援所有等同於 C 的類型、結構、回呼和關閉,是將 Perl 連結至 BLAS、LAPACK、HDF5 或任何其他原生程式庫的現代化方式。
Modern Perl 不是你的祖父’s Perl
以下功能直接取自官方版本說明 (perl5360delta, perl5380delta, perl5400delta) 並由其達到 穩定 狀態或首次引入的版本組織。僅強調與資料科學和科學運算工作負載相關的功能。
Perl 5.36 — 2022 年 5 月
使用 v5.36— 功能組合現在會自動啟用使用警告除了使用嚴格。它也會停用間接方法呼叫語法與多維雜湊金鑰模擬,消除兩個常見的細微錯誤來源。指定的子常式簽章 *(自 5.36 起;自 5.20 起的實驗) * — 現在依名稱宣告函數參數 (選擇性預設值)。繁體中文
//=與||=預設值運算子已進一步新增至 5.38 中的簽名,允許觸發的預設值未定義或分別虛偽:
use v5.36;
sub clamp ($val, $lo = 0, $hi //= 1) {
$val < $lo ? $lo : $val > $hi ? $hi : $val;
}
**
異形類別實例運算子 ** (自 5.36 起穩定;5.32 導入) —$obj isa "ClassName"傳回布林值;清除時間比ref ($obj) 等級"ClassName".**
內建模組 ** (自 5.40 起穩定;自 5.36 起的實驗) — 直接內建於解譯器的詞彙可匯入功能。穩定的 5.40 組合包包括 (其中包括):天花板,地板— 無整數捨入使用 POSIX.修剪— 從字串分割前置 / 尾端空格。已編製索引— 將每個元素與其索引配對;idiomatic companion to multi-value針對迴圈 (請參閱下文)。真偽,假,is_bool— 輸入布林值句子;序列器現在可以發出 JSON真偽/假而非1 個/0.弱勢,未減弱,is_weak— 用於建立雙向資料結構且不會發生記憶體流失的參照計數控制。束縛,網型,重新加載— 參考自我檢查。
穩定的布林值追蹤 *(5.36) * — 以布林值建立的純量 (例如,
!!1) 現在透過指派來保留其布林性質,以實現對 JSON 和 MessagePack 的可靠類型感知序列化。多重值
針對迴圈 *(自 5.40 起穩定;自 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) { ... }
延緩blocks (自 5.36 起即為實驗性) — 範圍結束保護,可在區塊結束時 (無論是一般還是透過異常狀況) 無條件地執行清除程式碼 - 自然取代以破壞者為基礎的範圍保護物件,以及資料管線中資源管理的重要模式。
Perl 5.38 — 2023 年 7 月
**
PERL_RAND_SEED環境變數 ** (5.38) — 在執行前設定此變數,每個蘭特呼叫 (沒有明確的)砂漿) 會產生相同的順序,啟用 可重現 隨機演算法 — 模擬、隨機抽樣、Monte Carlo 方法 — 而無需修改原始程式碼。**
類別/欄位/方法語法 ** (自 5.38 以來的實驗性) — 專為特定目的而設計的詞彙化物件系統,不需要無聊不@ISA或任何 CPAN 模組。可用於定義類型值物件,例如資料集資料列、模型參數或管線階段:
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 月
- **
嘗試/捕捉例外處理 ** (自 5.40 起穩定;自 5.34 起實驗;最後區塊以 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
}
(嘗試::Tiny / 特色::Compat::Try 只有當定位超過 5.34 的磁柱時才需要使用。).
多重值
針對迴圈 *(自 5.40 起穩定) * — 請參閱上方 5.36 項目;它們在本版本中從實驗中畢業到穩定。內建::inf與內建::nan(實驗自 5.40 起) — 輸入的浮點數無限和非數字常數,消除9**9**9或 POSIX hacks in numerical code。**
^^邏輯 XOR 運算子 ** (5.40) — 完成中優先順序邏輯運算子集 (&&,||,^^);適用於布林遮罩作業。**
使用 v5.40匯入內建函數 **— 超出啟用功能組合,使用 v5.40也會匯入對應的內建版本組合,使所有穩定內建::函數可用為不含個別名稱的簡稱使用內建敘述句。
長效功能 (5.36 之前).
說與狀態(自 5.10 起) —說是列印以隱含的換行;狀態宣告在內含子 (輕量型備忘原始類型) 的呼叫中持續存在的詞彙。一流的參考和結案 — 匿名子項、結案和參考構建都是基本的,而且自 Perl 5 起穩定。
使用常數或 CPAN唯讀已命名常數的模組;唯讀強制執行深度的不可變性使用常數不是。
結合與紅外線 或普倫夫 版本管理與紙箱 對於可重現的依賴性快照,現代的 Perl 專案看起來和一流的軟體工程工作一樣。
誠信限制
沒有任何案例可以讓 Perl 完全不誠實地知道它在哪裡很短:
視覺化 — Perl 沒有相等的項目
ggplot2或Matplotlib。繪圖通常需要對 R、gnuplot 或 Web 程式庫進行外部呼叫。有時此弱點會成為實際的強度,允許使用 Perl5 作為協調和增強其他動作者的應用程式語言。社群力量 — 數據科學社群融合了 Python 和 R。尋找現成的教學課程、Stack Overflow 回答,以及共同作者則較難。
物件方向 — 若無 Moose/Moo,OOP 模型為詳細;其中會新增相依性。新功能
類別功能可能解決這些問題大規模輸入安全 — 核心語言’s 動態純量會讓較大的協作數字程式碼庫更難推理 (請參閱下一節)。
2.Perl Data-Type System — 優勢和快取時代限制
核心 Perl 類型
佩爾’三個建構的基礎資料模型中心:
| 建構 | Sigil | 它擁有什麼 |
|---|---|---|
| 定量 | $ |
單一值:數字、字串、參照或未定義 |
| 陣列 | @ |
依整數編製索引的已排序純量清單 |
| 雜湊 | % |
依字串輸入之純量值的未排序集合 |
其他所有項目 (物件、結案、複雜的資料結構) 都是透過 參考資料 從這三個基礎元構建而成 (\@array, \% 雜湊, 下標{ ... }).
此模型具有額外的彈性。單一陣列可以同時保存整數、浮點數、字串以及巢狀參照。這種靈活性正是 Perl 20 年來主導的系統管理和 Web 文稿語言。
cache-hierarchy 問題
只有在資料流經 L1/L2/L3 快取時,現代 CPU 才會達到尖峰傳輸量† 在大型的連續區塊中 — 一個名為 spatial locality 的屬性。Perl 陣列未提供此項目。在背後,Perl 陣列是 pointers 的 C 陣列,用於堆集配置的純量 (歷史) 結構。每個純量都有參照計數、類型標記和填補 — 在 64 位元的建置上,每個純量通常為 24 到 56 個位元組。因此,重複超過百萬個元素的 Perl 陣列,涉及整個堆集散佈的百萬個指標取消參照,產生完全忽略現代 SIMD 管線的速度優勢的快取遺漏樣式。
具體後果:以純 Perl 撰寫的兩個 1 000 元素向量的點產品大約比對 PDL 浮點數陣列的同等作業慢 **100 – 1000 × **1000 × **1000 × **1000 × **,該陣列佔用兩個平坦、4 000 位元組記憶體區域,適合 L1 快取。
對比 R
R 佔據了好奇的中間地。就像 Perl 一樣,它是動態且解譯的語言 — 變數是未鍵入的容器,函數是一流的值,而互動式 REPL 則是主要開發環境。R 甚至有直接類比到 Perl’三核心類型:
| Perl 概念 | R 類比 |
|---|---|
$scalar |
length-1 原子向量或純量列表 |
@array |
清單 () |
%hash |
名稱清單 () |
參考 (\@arr) |
R 不使用明確的參照;改為複製修改語意 |
但 R’s workhorse 類型,例如 原子向量 沒有直向的 Perl 對應項。R 原子向量是連續的,同質上打字的記憶體區塊 — CPU 快取所獎勵的佈局 。R 中的每個內建純量實際上都是一個長度為 1 的原子向量;沒有”裸純量” 不在原子向量 。
此設計選擇意謂著 R 程式碼會以 BLAS 層次傳輸量的數百萬倍,自然在向量上運作,而不需要使用者編寫單一迴圈或配置特殊的迴圈。”陣列” 物件。
零售’單元類型為:
| R 原子類型 | 儲存 | C 等效 |
|---|---|---|
邏輯 |
4 個位元組 / 元素 | 整數 Data type (含 NA 片語) |
整數 |
4 個位元組 / 元素 | int32_t |
雙重 |
8 個位元組 / 元素 | 雙重 |
複合 |
16 個位元組 / 元素 | 複雜雙精度浮點數 (_C) |
字元 |
指向 CHARSXP | 字元 * (實習) |
原始 |
1 個位元組 / 元素 | uint8_t |
R 也定義建構在原子向量上的高階結構:
- 矩陣 — 具有 2D 原子向量的
二維屬性。 - 陣列 — N-D 原子向量與
二維屬性。 - data.frame — 具名的等長原子向量清單;lingua franca of
R 中的表格式資料。 - 因素 — 含有 1 個整數向量
階層屬性;將類別資料編碼。
課程:R’在統計和資料科學應用程式中使用時,計算效能會直接從其連續的原子向量流出。佩爾’對等的效能路徑為擴充 (亦為獨立路徑) 墊片 如環境),Perl 資料語言PDL.
3. 輸入 PDL:強烈鍵入的 N 維度陣列
Perl Data Language (PDL、pdl.perl.org) 以 ndarrays (N-dimensional Arrays) 擴充 Perl:連續、強式輸入的記憶體緩衝區,其外觀與感覺就像一流的 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 會將 C 數值類型的完整選用區顯示為一流的建構子:
| PDL 類型 | 位元組 | C 類型 | 建構子 |
|---|---|---|---|
位元組 |
1 | uint8_t |
位元組 (...) |
簡短 |
2 | int16_t |
短 (...) |
期別 |
2 | uint16_t |
ushort (...) |
長整數 |
4 日 | int32_t |
長 (...) |
索引 |
4 或 8 | ssize_t |
indx (...) |
長長 |
8 | int64_t |
長隆 (...) |
浮點 |
4 日 | 浮點 |
浮點 (...) |
雙重 |
8 | 雙重 |
雙倍 (...) |
浮動 |
8 | 複合浮動 (_C) |
cfloat (...) |
雙倍 |
16 | 複雜雙精度浮點數 (_C) |
cdouble (...) |
執行緒與 SIMD
PDL 其中之一’最明顯的功能是 *隱含執行緒 *:作業會自動透過額外的維度廣播,排除使用者程式碼中的明確迴圈,並將內部迴圈委派給最佳化的 C 或 Fortran 核心。結合與set_autopthread_targ (N),PDL 將自動平行多個獨立磁碟片段否 作業系統執行緒 — 不讓使用者編寫單一執行緒叉 或執行緒::佇列 呼叫。
值錯誤
PDL 有內建的 錯誤值概念 (PDL::錯誤),直接類似於 R’秒鐘不適用。可以將 ndarray 標示為”值感知錯誤”,PDL 作業會透過算術、統計資料及 I/O 正確傳輸錯誤。
4. 類型比較:Perl、PDL 和 R 並排
下表將每個常用的 R 類型對應至其最接近的 Perl 和 PDL 對等部分,強調三種語言彼此同意、不同或互補的位置。
| R 類型 | Perl 等效 | PDL 等效 | 備註 |
|---|---|---|---|
雙重 (長度 -1) |
$x = 3.14 (純量) |
雙倍 (3.14) — 形狀() |
R 沒有稀有純量;一切都是向量 |
整數 (長度 -1) |
$n = 42 (純量) |
長 (42) |
|
邏輯 (長度 -1) |
$flag = 1 / $flag = 0 |
位元組 (1) |
Perl 使用真實性;PDL 使用 0/1 位元組 |
雙重 向量 |
@arr = (1.1、2.2、3.3) |
雙倍 (1.1、2.2、3.3) |
PDL:連續;@arr:指標陣列 |
整數 向量 |
@arr = (1、2、3) |
長 (1、2、3) |
|
邏輯 向量 |
@flags = (1、0、1) |
位元組 (1,0,1) |
|
複合 向量 |
— (無內建) | cdouble (...) |
Perl 需要數學::複雜;PDL 具有原生支援 |
字元 向量 |
@strs = ('一個','b') |
— (非數字) | PDL 僅對數字運作 |
原始 向量 |
包裝 ('C*', @bytes) |
位元組 (...) |
|
不適用 |
未定義 |
ndarray 中的錯誤值 | PDL 錯誤值傳輸,例如 R’秒鐘不適用 |
空值 |
未定義 在清單相關資訊環境中 |
— | |
清單 |
@array 或參考\@array |
— | |
名稱清單 |
%hash 或\% 雜湊 |
— | |
矩陣 (2-D) |
陣列@aoa |
2D ndarray pdl ([[...],[...]]) |
PDL:資料欄主修;R:資料欄主修 |
陣列 (N-D) |
巢狀參照 | N-D ndarray $x-> 新產品 (...) |
|
data.frame |
%hash 總共@arrays |
2-D ndarray (數值資料欄) + Perl 雜湊 (混合) | 沒有完全相同的單一 PDL 類型對應 |
因子 |
雜湊查尋表格 + @indices |
長整數 ndarray + Perl @levels 陣列 |
|
環境 |
%hash 或套裝程式命名空間 |
— | |
函數 / 關閉 |
下標{ ... } / close |
— | PDL PP 定義已編譯的核心 |
S3 / S4 物件 |
blessed reference + method dispatch | PDL 物件 (繫結的 ndarray) | PDL 物件是第一個類別 Perl 物件 |
關鍵概要
對於 純數字、同質資料 (向量、矩陣、張數)、PDL ndarray 和 R 原子向量的功能同等且相對有效率。
若為 異質列表化資料 (混合類型、字串資料欄、因子),R’秒鐘
data.frame較符合人體工學;Perl 通常會使用陣列雜湊或專用的模組,例如資料::框架或PDL::IO::CSV.適用於 **文字、不規則結構及系統膠水 **、Perl’原生類型優於 R 和 Python。
因此,Perl+PDL 的組合提供了 R 提供的 union 作為統計語言,而 Perl 以系統語言提供的項目 — 以較陡的學習曲線和較少立即可用的 nd 法郎有限的統計工具。
不過,Perl+PDL+R 的組合 (後者用作元件,或儀器化 透過 Perl).
5. 藍圖:此系列的其餘部分涵蓋
此系列記錄了從頭開始建構 Perl5 + PDL 內建的 **向量資料庫引擎 **。向量資料庫是現代化的檢索增強生成 (RAG) 管線、語意搜尋和最近的鄰居建議系統。從第一項原則中導入一項是展示 PDL 的絕佳工具’數值功能與 Perl’s 系統程式設計強度。
與這些張貼項目共同開發的目錄包含下列元件,每個元件都會成為一或多個將參照專用儲存區域中檔案之專用張貼項目的主體。
Post 1 — 序列化和 I/O:VectorIO 模組
檔案:VectorIO.pm
該引擎將向量儲存為內裝的二進制小點MessagePack 有效負載。本文涵蓋:
- 設計一個乾淨的模組
出口商- 基礎公用 API使用 v5.40. - 在系統界限強制實行綱要正確性的驗證協助程式。
Post 2 — 模擬向量資料庫
檔案:simulate_vectorDB.pl
在搜尋資料庫之前,我們需要一個資料庫。此文章顯示:
- 產生可再現的隨機浮點向量與
PDL::隨機. - 使用
GetOpt::龍用於人體工學 CLI 選項剖析。 - 寫一個
-- 種子- 受控制的模擬會在執行時產生相同的資料庫,對於基準而言至關重要。
第 3 篇文章 — 標竿:timing_DB 模組
檔案:timing_DB.pm
績效申請需要評量。本文介紹:
- 內建可重複使用的 Perl 基準線束
時間::HiRes. - Perl/PDL 與 R 實作之間公平壁鐘比較的方法。
- 解譯不同工作負載大小的傳輸量 (向量 / 秒) 與延遲 (毫秒 / 查詢)。
Post 4 — K-Means 叢集與PDL::統計::代表
檔案:kmeans.pl
K-means clustering 是反向檔案索引 (IVF) 方法的骨幹,其近似鄰近的搜尋。本文涵蓋:
- The
PDL::統計::代表介面及其退貨合約 (中心,叢集,n 個,R2,秒). - 解譯
[日本人無修正]成員遮罩傳回者run_kmeans. - 比較 Perl/PDL k-means 中心與 R’秒鐘
Kmeans()與ClusterR::MiniBatchKmeans()以驗證數值正確性。
Post 5 — Mini-Batch K-Means:擴展至大型資料集
檔案:compare_kmeans_centroids.pl
完整 k-means 需要在記憶體中執行所有資料以進行每次重複。Mini-batch k-means 會交易少量的甲狀腺準確度,以大幅減少記憶體和運算。這篇文章探索:
- 在 PDL 中實作真正的重新取樣迷你批次迴圈。
- 量身定制全迷你批次變體之間的甲狀腺漂移。
- R 的並排輸出’秒鐘
MiniBatchKmeans從ClusterR套件。
Post 6 — 反轉檔案索引 (IVF) 搜尋
檔案:compare_ivf_search.pl
有了甲狀腺功能,我們可以分割資料庫,並執行近似鄰近搜尋的子線性。本文涵蓋:
- 建立反轉清單:將每個資料庫向量對應至其最接近的形心。
- The
unpack_inverted_lists協助者進入VectorIO. - Querying: 尋找最接近 K 的甲狀腺,然後只搜尋這些清單。
- 準確度與加速權衡,因為試用名單數量不同。
Post 7 — 對 R 進行驗證:數字正確性和跨語言管線
檔案:compare_kmeans_centroids.R, compare_kmeans_centroids_pure.R, plot_centroid_coordinates.R
基礎系列的最後一個文章關閉了 Perl 與 R 之間的迴路:
- 將 PDL 結果匯出成 CSV 並在 R 中讀取,以進行獨立驗證。
- 使用 ggplot2 可同時從兩種語言視覺化甲狀腺座標。
- 工作流程模式”Perl 中的運算,R 中的視覺化” 利用兩個生態系統的優勢。
下一步 — 過帳 1: 序列化與 I/O 搭配
VectorIO.pm
**† **現代 CPU 在處理器核心與主要 RAM 之間具有多個稱為 caches (L1、L2、L3) 的快速晶片內建記憶體。L1 是最小的 (每個核心通常為 32 – 64 KB) 和最快的 (1 – 4 時鐘週期延遲);L2 較大 (256 KB – 1 MB) 且速度較慢;L3 可在仍有較高延遲的核心 (4 – 64 MB) 之間共用。主要 RAM 延遲為 60 – 100 ns,比 L1 慢約 200 倍。
當運算以可預測的連續模式處理記憶體時,硬體 prefetcher 可以在需要之前將即將到來的資料載入 L1/L2,以達到接近尖峰的輸送量。散佈的指標連接 (例如周遊 Perl 堆集配置的定量陣列) 會擊敗預先擷取,並在等待從 RAM 解析每個快取遺漏時將 CPU 停滯。
