Perl5 como lenguaje de ciencia de datos

[FINALIZADO] Última actualización por en vie., 17 abr. 2026    origen
 

Ciencia de datos

Introducción a la serie — Posterior 0 de N
Este post es el primero de una serie que documenta el co-desarrollo de un motor de base de datos vectorial (VDBE) escrito enteramente en Perl5 + PDL. Las publicaciones posteriores recorren todos los componentes de ese motor; éste establece el escenario. El ímpetu principal de esta serie NO es que vuelques tu VDBE, ya que no hago afirmaciones de rendimiento, sino que muestres cómo se puede usar Perl para lograr prácticamente cualquier cosa que puedas lograr con cualquier otro idioma, ¡pero más inteligente!


Tabla de contenido


1. ¿Por qué Perl5 para Data Science?

Cuando los científicos de datos discuten las opciones de idioma, la conversación converge rápidamente en Python, R o Julia. Perl5 rara vez tiene un asiento en la mesa, sin embargo, lleva un conjunto convincente de rasgos que merecen una segunda mirada. Estos rasgos no han cambiado sustancialmente a lo largo de los años (Perl5 siempre ha sido así), pero a menos que Usted ha estado expuesto al lenguaje y ha aprendido a apreciar su tercidad, racionalidad, flexibilidad, expresibilidad y realmente lo ha utilizado para impulsar su trabajo hacia adelante., no sabría que estas funciones no solo vienen de forma gratuita con Perl5, sino que pueden ayudarlo a impulsar sus proyectos.

Ubiquity y despliegue sin instalación

Perl5 se incluye como componente predeterminado de prácticamente todos los sistemas operativos similares a UNIX: distribuciones de Linux, macOS, BSD y muchos entornos de Linux integrados, todos incluyen un entorno de trabajo perl binario listo para usar. Python ha estado haciendo incursiones aquí, pero aún es común encontrar servidores sin cabecera, dispositivos de red o nodos de inicio de sesión de HPC.
donde Perl está presente y una pila completa de Python no lo está. Un pipeline de datos escrito en Perl se puede ejecutar el primer día sin un conda medio ambiente, un venvo un contenedor.

Portabilidad desde el centro de datos hasta el borde

La misma secuencia de comandos que analiza un conjunto de datos de terabytes en un nodo HPC de 256 núcleos puede, con cambios de configuración menores, ejecutarse en una Raspberry Pi, una puerta de enlace IoT o un controlador incrustado. Perl’El modelo de despliegue binario único y la baja sobrecarga de tiempo de ejecución lo convierten en un auténtico “escribir una vez, ejecutar en cualquier lugar” lenguaje en entornos donde Python’Sobrecarga del intérprete o Julia’El tiempo de calentamiento del JIT sería inaceptable.

Si planea desplegar en cualquier lugar y everywhere Perl5 es su elección obvia.

Una herencia basada en el texto y la mungación de datos

Perl fue diseñado desde cero para el procesamiento de texto, expresiones regulares y “pegamento” trabajo entre los componentes del sistema. En la práctica, los pipelines de datos científicos no están dominados por el cálculo numérico, sino por la conversación de datos: lectura de formatos de archivo heterogéneos, limpieza de registros desordenados, unión de conjuntos de datos de diferentes fuentes y enrutamiento de resultados a componentes de consumo descendente.

Perl’El motor regex sigue siendo uno de los más potentes disponibles, y las líneas únicas pueden realizar tareas de limpieza de datos que requerirían bibliotecas auxiliares en otros idiomas.

Si usted está en el dominio de la informática científica, es posible que haya encontrado la noción de sistemas de gestión del flujo de trabajo e investigación reproducible. Ambos se basan en la ejecución de transformaciones de datos integrales y el flujo de trabajo para eliminar las actividades manuales, propensas a errores y tediosas de puntos y clic que los analistas y científicos tienen que hacer para transformar sus datos en insights e inferencias, respectivamente.

En este nuevo mundo, Perl5’s rico historial le permite brillar tanto como un componente de los flujos de trabajo, o como un lenguaje de aplicación que implementa estos flujos de trabajo.

CPAN: un ecosistema de módulos probado en batalla

La Comprehensive Perl Archive Network (CPAN) aloja más de 200.000 módulos en todos los dominios imaginables. Si bien las ofertas de ciencia de datos no son tan extensas como Python, los componentes básicos para los constructores dedicados están allí.:

Modern Perl no es tu abuelo’s Perl

Las siguientes características se extraen directamente de las notas de lanzamiento oficiales (perl5360delta, perl5380delta, perl5400delta) y organizados por el lanzamiento en el que alcanzaron el estado estables o se introdujeron por primera vez. Solo se destacan las funciones relevantes para las cargas de trabajo de ciencia de datos y computación científica.

Perl 5.36 — Mayo de 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

O tome varios valores al mismo tiempo

  use v5.40;

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

Perl 5.38 — julio de 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 — Junio de 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
  }

(Prueba::Tiny / Característica::Compat::Try solo son necesarios cuando se dirigen a perlas mayores de 5,34.)

Características de larga duración (pre-5.36)

Referencias y cierres de primera clase: los subs anónimos, los cierres y la construcción de referencia son fundamentales y se han mantenido estables desde Perl 5.

Combinado con perlbrew o plenario para la gestión de versiones y cartón para instantáneas de dependencia reproducibles, un proyecto moderno de Perl se ve y se siente como un esfuerzo de ingeniería de software de primera clase.

Limitaciones honestas

Ningún caso de Perl está completo sin honestidad sobre dónde se queda corto:


2. El sistema Perl Data-Type: fortalezas y límites de Cache-Era

Tipos de perl principal

Perl’El modelo de datos fundamental se centra en tres construcciones:

Construir Sigil Lo que tiene
Escalar $ Un solo valor: número, cadena, referencia o sin definir
Matriz @ Lista ordenada de escalares, indexada por entero
Hash % Recopilación no ordenada de valores escalares tecleados por cadena

Todo lo demás, objetos, cierres, estructuras de datos complejas, se construye a partir de estos tres primitivos a través de referencias (\@array, \%hash, sub { ... }).

Este modelo es extraordinariamente flexible. Una única matriz puede contener números enteros, números de coma flotante, cadenas y referencias anidadas simultáneamente. Esa flexibilidad es exactamente lo que hizo de Perl el lenguaje dominante de administración de sistemas y scripts web durante dos décadas.

Problema de jerarquía de caché

Las CPU modernas logran un rendimiento máximo solo cuando los datos fluyen a través de la caché L1/L2/L3 en bloques grandes y contiguos, una propiedad llamada localidad espacial. Las matrices de perl no proporcionan esto. Bajo el capó, una matriz de Perl es una matriz C de pointers al escalar asignado al montón (SV) estructuras. Cada escalar lleva un recuento de referencias, una etiqueta de tipo y relleno, normalmente de 24 a 56 bytes por escalar en una compilación de 64 bits. Por lo tanto, iterar más de un millón de elementos de la matriz de Perl implica un millón de referencias de puntero dispersas a través de la pila, produciendo un patrón de pérdida de caché que niega por completo la ventaja de velocidad de los pipelines SIMD modernos.

Una consecuencia concreta: un producto punto de dos vectores de 1 000 elementos escritos en Perl puro es aproximadamente 100-1000× más lento que la operación equivalente en un par de ndarrays flotantes PDL, que ocupan dos regiones de memoria plana de 4 000 bytes que caben cómodamente en la caché L1.

Contraste con R

R ocupa un curioso punto medio. Al igual que Perl, es un lenguaje dinámico e interpretado: las variables son contenedores sin tipo, las funciones son valores de primera clase y la REPL interactiva es el entorno de desarrollo principal. R incluso tiene análogos directos a Perl’tres tipos principales:

Concepto de perl Análogo R
Escalar $ longitud-1 vector atómico o escalar en lista
@array lista()
%hash nombrado lista()
Referencia (\@arr) R no utiliza referencias explícitas; en su lugar, la semántica de copia en modificación

Pero R’s tipo de caballo de batalla, es decir, el vector atómico no tiene contraparte directa de Perl. Un vector atómico R es un bloque de memoria contiguo y de tipo homogéneo, exactamente el diseño que recompensa una caché de CPU. Cada escalar incorporado en R es en realidad un vector atómico longitud-1; no hay “escalar simple” fuera de los vectores atómicos.

Esta elección de diseño significa que el código R funciona naturalmente en vectores de millones de dobles con rendimiento a nivel de BLAS, sin que el usuario escriba un solo bucle o asigne un bucle especial. “matriz” objeto.

R’tipos atómicos son:

Tipo atómico R Almacenamiento Equivalente C
lógico 4 bytes/elemento int (con el centinela NA)
entero 4 bytes/elemento int32_t
doble 8 bytes/elemento doble
complejo 16 bytes/elemento _Complejo doble
carácter puntero a CHARSXP carácter * (interrumpido)
crudo 1 byte/elemento uint8_t

R también define estructuras de nivel superior construidas sobre vectores atómicos:

La lección: R’El rendimiento informático cuando se utiliza en aplicaciones estadísticas y de ciencia de datos fluye directamente desde sus vectores atómicos contiguos. Perl’s ruta equivalente al rendimiento es una extensión (que también es un matlab como el medio ambiente), el lenguaje de datos Perl PDL.


3. Introducir PDL: Matrices N-Dimensionales de Tipo Fuerte

Lenguaje de datos de Perl (PDL, pdl.perl.org) amplía Perl con ndarrays ( matrices N-dimensionales): buffers de memoria contiguos y fuertemente tipados que se ven y se sienten como objetos Perl de primera clase.

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;

Tipos primitivos de PDL

PDL expone la paleta completa de tipos numéricos C como constructores de primera clase:

Tipo PDL Bytes Tipo C Constructor
byte 1 uint8_t byte(...)
corto 2 int16_t corto(...)
corto 2 uint16_t ushort(...)
largo 4 int32_t largo(...)
xx 4 u 8 ssize_t indx(...)
largo 8 int64_t largo(...)
flotante 4 flotante flotante(...)
doble 8 doble doble(...)
flotante 8 _Flotante complejo cfloat(...)
doble 16 _Complejo doble cdouble(...)

Enhebrado y SIMD

Uno de PDL’Las características más distintivas son hilo implícito: las operaciones se transmiten automáticamente a través de dimensiones adicionales, eliminando bucles explícitos en el código del usuario y delegando bucles internos a núcleos C o Fortran optimizados. Combinado con set_autopthread_targ(N), PDL paralelizará automáticamente porciones independientes N Threads del sistema operativo: sin que el usuario escriba un solo bifurcación o Thread::Cola llamada.

Valores incorrectos

PDL tiene un concepto incorporado de valores malos (PDL: Malo), directamente análogo a R’s N/D. Una matriz se puede marcar como “valor erróneo”, y las operaciones de PDL propagan el error correctamente a través de la aritmética, las estadísticas y la E/S.


4. Comparación de tipos: Perl, PDL y R lado a lado

La siguiente tabla asigna cada tipo R de uso común a sus homólogos más cercanos de Perl y PDL, destacando dónde los tres idiomas están de acuerdo, difieren o se complementan entre sí.

Tipo R Equivalente en perl Equivalente en PDL Notas
doble (longitud-1) $x = 3,14 (escalar) doble(3.14) — forma () R no tiene escalar desnudo; todo es un vector
entero (longitud-1) $n = 42 (escalar) largo(42)
lógico (longitud-1) $flag = 1 / Indicador de $ = 0 byte(1) Perl utiliza la veracidad; PDL utiliza 0/1 byte
doble vector @arr = (1.1, 2.2, 3.3) doble(1.1, 2.2, 3.3) PDL: contiguo; @arr: matriz de punteros
entero vector @arr = (1, 2, 3) largo(1, 2, 3)
lógico vector @flags = (1, 0, 1) byte(1, 0, 1)
complejo vector — (no incorporado) cdouble(...) Necesidades de perl Matemáticas::Complejo; PDL tiene soporte nativo
carácter vector @strs = ('a','b') — (no numérico) La PDL sólo funciona con números
crudo vector paquete('C*', @bytes) byte(...)
N/D sin definir Valor negativo en ndarray Los valores negativos de PDL se propagan como R’s N/D
NULL sin definir en el contexto de la lista
lista @array o referencia \@array
nombrado lista %hash o \%hash
matriz (2-D) matriz de relatos @aoa 2-D ndarray pdl([[...],[...]]) PDL: columna mayor; R: columna mayor
matriz (N-D) referencias anidadas N-D ndarray $x->reforma(...)
data.frame %hash de @arrays 2-D ndarray (cols numéricos) + hash perl (mezclado) No hay mapas de tipo PDL exactamente
factor tabla de búsqueda hash + @indices largo ndarray + perl @levels matriz
entorno %hash o espacio de nombres de paquete
función / cierre sub { ... } / cierre PDL PP define los núcleos compilados
S3 / S4 objeto Referencia bendita + envío del método Objeto PDL (ndarray bendito) Los objetos PDL son objetos Perl de primera clase

Conclusiones clave

Sin embargo, la combinación de Perl+PDL+R (con este último utilizado como componente, o instrumentalizado vía Perl)


5. Hoja de ruta: lo que cubre el resto de esta serie

Esta serie documenta la construcción de un motor de base de datos vectorial incorporado en Perl5 + PDL desde cero. Las bases de datos vectoriales respaldan los pipelines modernos de generación aumentada de recuperación (RAG), la búsqueda semántica y los sistemas de recomendación de vecinos más cercanos. Implementar uno de los primeros principios es un excelente vehículo para demostrar PDL’s capacidades numéricas junto con Perl’puntos fuertes de la programación de sistemas.

El directorio co-desarrollado junto a estas publicaciones contiene los siguientes componentes, cada uno de los cuales será objeto de una o más publicaciones dedicadas que harán referencia a archivos en un repositorio dedicado.

Post 1 — Serialización y E/S: el VectorIO módulo

Archivo: VectorIO.pm

El motor almacena vectores como bloques binarios empaquetados dentro MessagePack cargas útiles. Este post cubre:

Publicación 2 — Simulación de una base de datos vectorial

Archivo: simulate_vectorDB.pl

Antes de buscar una base de datos, necesitamos una. Este post muestra:

Post 3 — Benchmarking: el timing_DB Módulo

Archivo: timing_DB.pm

Las reclamaciones de rendimiento requieren medición. Este post presenta:

Publicación 4 — Clusters de K-medias con PDL::Estadísticas::Kmeans

Archivo: kmeans.pl

La agrupación en K-means es la columna vertebral del enfoque del índice de archivos invertidos (IVF) para aproximar la búsqueda de vecinos más cercanos. Este post cubre:

Post 5 — Mini-Batch K-Means: Escalado a grandes conjuntos de datos

Archivo: compare_kmeans_centroids.pl

k-means completo requiere todos los datos en la memoria para cada iteración. Mini-lote k-means negocia una pequeña cantidad de precisión centroide para una gran reducción en la memoria y la computación. Este post explora:

Publicación 6 — Búsqueda de índice de archivos invertidos (IVF)

Archivo: compare_ivf_search.pl

Con los centroides en la mano podemos particionar la base de datos y realizar una búsqueda sublineal aproximada de vecinos más cercanos. Este post cubre:

Post 7 — Validación contra R: corrección numérica y pipelines en varios idiomas

Archivos: compare_kmeans_centroids.R, compare_kmeans_centroids_pure.R, plot_centroid_coordinates.R

El último post de la serie de fundación cierra el bucle entre Perl y R:


Siguiente — Publicación 1: Serie y E/S con VectorIO.pm


Las CPU modernas tienen varios niveles de memoria rápida en chip llamada cachés (L1, L2, L3) que se encuentran entre los núcleos del procesador y la RAM principal. L1 es el más pequeño (normalmente de 32 a 64 KB por núcleo) y el más rápido (de 1 a 4 ciclos de reloj de latencia); L2 es más grande (256 KB a 1 MB) y ligeramente más lento; L3 se comparte entre núcleos (de 4 a 64 MB) con una latencia aún mayor. La RAM principal se encuentra más lejos en latencia de 60-100 ns, aproximadamente 200 veces más lenta que L1.

Cuando un cálculo toca la memoria en un patrón predecible y contiguo, el hardware prefetcher puede cargar los próximos datos en L1/L2 antes de que sea necesario, logrando un rendimiento casi máximo. La búsqueda de punteros dispersos (como recorrer una matriz Perl de escalares asignados a pilas) derrota la recuperación previa, deteniendo la CPU mientras espera a que cada falta de caché se resuelva desde RAM.