Perl5 som datavetenskapsspråk

[AVSLUTAD] Senast uppdaterad av Fri, 17 Apr 2026    källa
 

Datavetenskap

Serieintroduktion – Post 0 av N
Det här inlägget är det första i en serie som dokumenterar samutvecklingen av en vektor-databasmotor (VDBE) skriven helt i Perl5 + PDL. Senare inlägg går igenom varje komponent i den motorn; den här sätter scenen. Den viktigaste drivkraften för denna serie är INTE att du dumpar din VDBE eftersom jag inte gör några prestandakrav, men för att visa hur man kan använda Perl för att uppnå i stort sett allt du kan uppnå med något annat språk, men smartare!


Innehållsförteckning


1. Varför Perl5 för datavetenskap?

När dataforskare diskuterar språkval konvergerar konversationen snabbt på Python, R eller Julia. Perl5 får sällan en plats vid bordet - men det bär en övertygande uppsättning egenskaper som förtjänar en andra titt. Dessa egenskaper har inte förändrats väsentligt genom åren (Perl5 har alltid varit så!), men om inte Du har blivit utsatt för språket och lärt dig att uppskatta dess tercity, rationalitet, flexibilitet, uttrycksfullhet och faktiskt använt det för att driva ditt arbete framåt., du skulle inte veta att dessa funktioner inte bara kommer gratis med Perl5, men kan hjälpa dig att driva dina projekt framåt.

Ubiquity och driftsättning utan installation

Perl5 levereras som standardkomponent i praktiskt taget alla UNIX-liknande operativsystem – Linux-distributioner, macOS, BSD:er och många inbäddade Linux-miljöer innehåller alla en fungerande möjligen Binär ur lådan. Python har gjort inbrytningar här, men det är fortfarande vanligt att hitta huvudlösa servrar, nätverksenheter eller HPC-inloggningsnoder
där Perl är närvarande och en full Python stack är inte. En datapipeline skriven i Perl kan köras dag ett utan en conda miljö, a venveller en container.

Portabilitet från datacentret till kanten

Samma skript som analyserar en terabyte-datamängd på en 256-kärnig HPC-nod kan, med mindre konfigurationsändringar, köras på en Raspberry Pi, en IoT-gateway eller en inbäddad styrenhet. Perl’en binär distributionsmodell och låga administrationskostnader gör den till en äkta “skriv en gång, kör var som helst” Språk i miljöer där Python’s tolk overhead eller Julia’JIT uppvärmningstid skulle vara oacceptabelt.

Om du planerar att distribuera var som helst och everywhere Perl5 är ditt självklara val.

Ett arv byggt på text- och datamunging

Perl har utformats från grunden för textbearbetning, reguljära uttryck och “lim” arbete mellan systemkomponenter. I praktiken domineras vetenskapliga datapipeliner inte av numerisk beräkning utan av dataskryptering: läsning av heterogena filformat, rengöring av röriga poster, koppling av datamängder från olika källor och dirigering av resultat till nedströmskrävande komponenter.

Perl’regex-motorn är fortfarande bland de mest kraftfulla tillgängliga, och en-liners kan utföra datarengöringsuppgifter som skulle kräva hjälpbibliotek på andra språk.

Om du är inom området vetenskaplig databehandling kan du ha stött på begreppet *arbetsflödeshanteringssystem *och *reproducerbar forskning *. De är båda beroende av exekveringen av heltäckande datatransformeringar och arbetsflöden för att eliminera de manuella, felbenägna och tråkiga peka och klicka-aktiviteter som analytiker och forskare måste göra för att omvandla sina data till insikter respektive slutsatser.

I denna modiga nya värld, Perl5’s rika historia gör det möjligt att lysa både som en komponent i arbetsflöden, eller som ett applikationsspråk som implementerar dessa arbetsflöden.

CPAN: ett stridstestat modulekosystem

Det omfattande Perl Archive Network (CPAN) är värd för över 200 000 moduler över alla tänkbara domäner. Medan datavetenskapserbjudandena inte är nästan lika omfattande som Python, finns de grundläggande komponenterna för dedikerade byggare där:

Modern Perl är inte din farfar’s Perl

Funktionerna nedan är hämtade direkt från den officiella releaseinformationen (perl5360delta, perl5380delta, perl5400delta) och organiseras av den utgåva där de uppnådde status stabil eller först introducerades. Endast funktioner som är relevanta för arbetsbelastningar inom datavetenskap och vetenskaplig beräkning markeras.

Perl 5.36 – maj 2022

  use v5.36;
  sub clamp ($val, $lo = 0, $hi //= 1) {
      $val < $lo ? $lo : $val > $hi ? $hi : $val;
  }
  use v5.40;
  use builtin 'indexed';

for my ($i, $val) (indexed @scores)  { ... } # index and value

Eller hämta flera värden samtidigt

  use v5.40;

for my ($val1, $val2, $val3) (@scores)  { ... }

Perl 5.38 – juli 2023

  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 – juni 2024

  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
  }

(Försök::Tiny / Funktion::Compat::Försök behövs endast vid målinriktning av perls äldre än 5,34.)

Långvariga funktioner (före 5.36)

Kombinerad med perlbreiska eller plenarförsamling för versionshantering och kartong För reproducerbara beroendeögonblicksbilder ser ett modernt Perl-projekt ut och känns som en förstklassig programvaruutveckling.

Ärliga begränsningar

Inget fall för Perl är komplett utan ärlighet om var det faller kort:


2. Perl Data-Type System – Styrkor och Cache-Era-gränser

Kärna perl-typer

Perl’s grundläggande datamodell centrerar på tre konstruktioner:

Konstruktion Sigil Vad den håller
Skalär $ Ett enstaka värde: tal, sträng, referens eller ofördelaktig
Uppställning @ En ordnad lista över skalärer, indexerade efter heltal
Hash % En osorterad samling skalära värden som anges med sträng

Allt annat - objekt, förslutningar, komplexa datastrukturer - byggs från dessa tre primitiver via *referenser *(\@array, \%hash, nedsänkt { ... }).

Denna modell är extremt flexibel. En enskild array kan innehålla heltal, flyttal, strängar och kapslade referenser samtidigt. Den flexibiliteten är precis vad som gjorde Perl till det dominerande systemadministrations- och webbskriptspråket i två decennier.

Problemet med cachehierarkin

Moderna processorer uppnår toppdataflöde endast när data flödar genom cachen L1/L2/L3 i stora, sammanhängande block - en egenskap som kallas spatial locality. Perl-matriser tillhandahåller inte detta. Under huven är en Perl-matris en C-matris med pekare till högallokerad skalär (SV) strukturerna. Varje skalär har ett referensantal, en typtagg och utfyllnad - vanligtvis 24-56 byte per skalär på en 64-bitarsversion. Iterera över en miljon-element Perl array innebär därför en miljon pekare avreferenser spridda över heap, vilket ger en cache-miss mönster som helt förnekar hastighetsfördelen med moderna SIMD rörledningar.

En konkret konsekvens: en punktprodukt av två 1 000-elementvektorer skrivna i ren Perl är ungefär 100-1000 × långsammare än motsvarande operation på ett par PDL-float ndarrays, som upptar två platta minnesregioner på 4 000 byte som passar bekvämt i L1-cachen.

Jämför med R

R upptar en märklig medelväg. Liksom Perl är det ett dynamiskt, tolkat språk - variabler är otypade behållare, funktioner är förstklassiga värden och den interaktiva REPL är den primära utvecklingsmiljön. R har även direkta analoger till Perl’s tre kärntyper:

Perl koncept R analog
$skalär längd-1 atomär vektor eller skalär-i-lista
@array lista()
% hash namngiven lista()
Referens (\@arr) R använder inte uttryckliga referenser; copy-on-modify semantik i stället

Men R’typen s workhorse, dvs. den atomiska vektorn har ingen enkel Perl-motsvarighet. En R-atomvektor är ett sammanhängande, homogent typat minnesblock - exakt den layout som en CPU-cache belönar. Varje inbyggd skalär i R är faktiskt en längd-1 atomär vektor; det finns ingen “blottad skalär” Utanför atomvektorer.

Detta designval innebär att R-kod naturligt fungerar på vektorer av miljontals dubbletter med BLAS-nivå genomströmning, utan att användaren skriver en enda slinga eller allokerar en speciell “uppställning” objekt.

R’atomtyperna är:

R atomär typ Lagring C ekvivalent
logisk 4 byte/element heltal (Omdirigerad från NA Sentinel)
heltal 4 byte/element int32_t
fördubbla 8 byte/element fördubbla
komplex 16 byte/element _Komplex dubbel
skrivtecken pekare till CHARSXP tecken * (internerad)
1 byte/element uint8_t

R definierar också högre strukturer byggda på atomvektorer:

Lärdom: R’beräkningsprestanda när den används i statistiska och datavetenskapliga applikationer flödar direkt från dess angränsande atomvektorer. Perl’motsvarande väg till prestanda är en förlängning (som också är en fristående matris som miljö), Perl Data Language PDL.


3. Ange PDL: Starkt typade N-dimensionella uppställningar

Perl-dataspråk (PDL, pdl.perl.org) utökar Perl med *ndarrays *(N-dimensionella matriser): sammanhängande, starkt skrivna minnesbuffertar som ser ut och känns som förstklassiga Perl-objekt.

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-primitiva typer

PDL visar hela paletten med numeriska C-typer som förstklassiga konstruktorer:

PDL typ Byte C typ Konstruktör
byte 1 uint8_t byte(...)
kortslutning 2 int16_t kort(...)
kohort 2 uint16_t Om oss(...)
hög 4 int32_t lång(...)
indx 4 eller 8 ssize_t indx(...)
lång 8 int64_t långa(...)
flyta 4 flyta flyta(...)
fördubbla 8 fördubbla dubbel(...)
toalett 8 _Komplex flyttal Cfloat(...)
dubbel 16 _Komplex dubbel dubbel(...)

Gängning och SIMD

En av PDL’s mest distinkta funktioner är *implicit trådning *: operationer sänds automatiskt över extra dimensioner, vilket eliminerar explicita slingor i användarkoden och delegerar inre slingor till optimerade C- eller Fortran-kärnor. Kombinerad med set_autopthread_targ(N), PDL parallelliserar automatiskt oberoende segment över N OS-trådar – utan att användaren skriver en enda vägskäl eller Tråd::Kö ring.

Felaktiga värden

PDL har ett inbyggt koncept med dåliga värden (PDL::Fel), direkt analogt med R’s Ej tillämpligt. En ndarray kan flaggas som “medveten om dåligt värde”, och PDL-åtgärder sprider ondska korrekt genom aritmetik, statistik och I/O.


4. Typjämförelse: Perl, PDL och R sida vid sida

Tabellen nedan kartlägger varje vanlig R-typ till sina närmaste Perl- och PDL-motsvarigheter, och belyser var de tre språken är överens, skiljer sig åt eller kompletterar varandra.

R typ Perl ekvivalent PDL ekvivalent Anteckningar
fördubbla (längd-1) $x = 3,14 (skalär) dubbel(3.14) — form () R har ingen skalär; allt är en vektor
heltal (längd-1) $n = 42 (skalär) lång(42)
logisk (längd-1) $flag = 1 / $flag = 0 byte(1) Perl använder sanningsenlighet; PDL använder 0/1 byte
fördubbla vektor @arr = (1.1, 2.2, 3.3) dubbel (1.1, 2.2, 3.3) PDL: sammanhängande; @arr: pekaruppställning
heltal vektor @arr = (1, 2, 3) lång(1, 2, 3)
logisk vektor @flags = (1, 0, 1) byte(1, 0, 1)
komplex vektor — (ingen inbyggd) dubbel(...) Perl behov Matematik::KomplexPDL har inbyggt stöd
skrivtecken vektor @strs = ('år','b)') — (inte numeriskt) PDL används endast på siffror
vektor pack('C*', @bytes) byte(...)
Ej tillämpligt ofördelaktig Dåligt värde i ndarray PDL dåliga värden sprids som R’s Ej tillämpligt
NULL ofördelaktig i förteckningssammanhang
lista @array eller referens \@array
namngiven lista % hash eller \%hash
matris (2-D) array-of-arrays @aoa 2D ndarray pdl([[...],[...]]) PDL: kolumn-huvud; R: kolumn-huvudämne
uppställning (N-D) kapslade referenser N-D ndarray $x->reshape(...)
data.frame % hash av @arrays 2-D ndarray (numeriska kolon) + Perl hash (blandad) Inga enskilda PDL-typkartor exakt
faktor hash söktabell + @indices hög ndarray + perl @levels uppställning
miljö % hash eller paketnamnrymd
funktion / stängning nedsänkt { ... } / stängning PDL PP definierar sammanställda kärnor
Objektet S3/S4 välsignad referens + metodutskick PDL objekt (välsignad ndarray) PDL objekt är förstklassiga Perl objekt

Viktiga insikter

Kombinationen av Perl+PDL+R (med den senare som komponent, eller instrumentaliserad via Perl)


5. Vägkarta: Vad resten av denna serie täcker

Den här serien dokumenterar konstruktionen av en vektordatabasmotor byggd i Perl5 + PDL från grunden. Vektordatabaser bygger på moderna RAG-pipeliner (hämtningsförstärkta generering), semantisk sökning och rekommendationssystem närmaste granne. Att genomföra en av de första principerna är ett utmärkt fordon för att demonstrera PDL’s numeriska förmågor tillsammans med Perl’Styrkor med systemprogrammering.

Katalogen som samutvecklas tillsammans med dessa inlägg innehåller följande komponenter, som var och en kommer att vara föremål för en eller flera dedikerade inlägg som kommer att referera till filer i ett dedikerat arkiv.

Post 1 – Serialisering och I/O: VectorIO modul

Fil: VectorIO.pm

Motorn lagrar vektorer som packade binära blobbar inuti MessagePack nyttolaster. Detta inlägg täcker:

Post 2 – Simulera en vektordatabas

Fil: simulate_vectorDB.pl

Innan vi kan söka i en databas behöver vi en. Det här inlägget visar:

Post 3 – Riktmärkning: timing_DB Modul

Fil: timing_DB.pm

Prestationsanspråk kräver mätning. Detta inlägg introducerar:

Post 4 — K-Means-klustring med PDL::Stat::Kmedel

Fil: kmeans.pl

K-medel klustring är ryggraden i inverterad fil index (IVF) tillvägagångssätt för ungefärlig närmaste granne sökning. Detta inlägg täcker:

Inlägg 5 — Mini-Batch K-Means: Skalning till stora datamängder

Fil: compare_kmeans_centroids.pl

Full k-means kräver alla data i minnet för varje iteration. Mini-batch k-medel handlar en liten mängd centroid noggrannhet för en stor minskning av minne och beräkning. Den här artikeln utforskar:

Inlägg 6 — Sökning efter index för inverterad fil (IVF)

Fil: compare_ivf_search.pl

Med centroider i handen kan vi partitionera databasen och utföra sublinjär ungefärlig närmaste granne sökning. Detta inlägg täcker:

Post 7 – Validering mot R: Numerisk korrekthet och korsspråkiga pipeliner

Filer: compare_kmeans_centroids.R, compare_kmeans_centroids_pure.R, plot_centroid_coordinates.R

Det sista inlägget i grundserien stänger slingan mellan Perl och R:


Nästa upp – Post 1: Serialisering och I/O med VectorIO.pm


Moderna processorer har flera nivåer av snabbt minne på chip som kallas cache (L1, L2, L3) som sitter mellan processorkärnorna och huvud-RAM. L1 är den minsta (vanligtvis 32-64 KB per kärna) och snabbaste (1-4 klockcykler latens); L2 är större (256 KB-1 MB) och något långsammare; L3 delas över kärnor (4-64 MB) med högre latens fortfarande. Huvud RAM sitter längre bort vid 60-100 ns latens - ungefär 200× långsammare än L1.

När en beräkning berör minnet i ett förutsägbart, sammanhängande mönster kan maskinvaran prefetcher ladda kommande data till L1/L2 innan det behövs, vilket ger ett nästan högt dataflöde. Spridd pekarsökning (t.ex. genom att korsa en Perl-matris med heap-allokerade skalärer) besegrar förhämtning, stoppar processorn medan den väntar på att varje cache-miss ska lösas från RAM.