2009-10-19 21 views
5

Tenemos un cuerpo maduro de código que carga datos de archivos en una base de datos. Hay varios formatos de archivo; todos son campos de ancho fijo.¿Cómo puedo acelerar el procesamiento de Perl de datos de ancho fijo?

Parte del código utiliza la función Perl unpack() para leer campos de los datos de entrada en variables de paquete. La lógica empresarial es capaz de referirse a estos campos de una manera 'legible por humanos'.

El código de lectura del archivo se genera a partir de una descripción de formato una vez, antes de leer un archivo.

En forma de bosquejo, el código generado se ve así:

while (<>) { 

    # Start of generated code. 

    # Here we unpack 2 fields, real code does around 200. 
    ($FIELDS::transaction_date, $FIELDS::customer_id) = unpack q{A8 A20}; 

    # Some fields have leading space removed 
    # Generated code has one line like this per affected field. 
    $FIELDS::customer_id =~ s/^\s+//; 

    # End of generated code. 

    # Then we apply business logic to the data ... 
    if ($FIELDS::transaction_date eq $today) { 
     push @fields, q{something or other}; 
    } 

    # Write to standard format for bulk load to the database. 
    print $fh join('|', @fields) . q{\n} or die; 
} 

Perfilado el código revela que alrededor de 35% del tiempo que se gasta en el desempaquetado y tiras-espacio inicial. El tiempo restante se usa para validar y transformar los datos y para escribir en el archivo de salida.

Parece que no hay una sola parte de la lógica empresarial que tome más del 1-2% del tiempo de ejecución.

La pregunta es: ¿podemos obtener un poco más de velocidad del desembalaje y del espacio extraído de alguna manera? Preferiblemente sin tener que refactorizar todo el código que hace referencia a las variables del paquete FIELDS.

EDIT:

En caso de que se hace una diferencia

$ perl -v 
This is perl, v5.8.0 built for PA-RISC1.1 
+0

Sería interesante saber si el uso de una lista de variables de paquete en el lado izquierdo del desempaquetado es probable que sea óptimo. –

Respuesta

1

Sí. La extracción con substr es probablemente la forma más rápida de hacerlo. Es decir:

$FIELDS::transaction_date = substr $_, 0, 8; 
$FIELDS::customer_id  = substr $_, 8, 20; 

es probable que sea más rápido. Ahora, si estuviera escribiendo a mano este código, no me rendiría en unpack, pero si está generando el código, podría darle una oportunidad y medirlo.

Ver también las respuestas a Is Perl’s unpack() ever faster than substr()?

En cuanto a pelar los espacios iniciales, s/^\s+// es probable que sea el método más rápido.

Actualización: Es difícil decir algo definitivo sin poder ejecutar puntos de referencia. Sin embargo, ¿qué hay de:

my $x = substr $_, 0, 8; 

para los campos que no necesitan ningún recorte y

my ($y) = substr($_, 8, 20) =~ /\A\s+(.+?)\s+\z/; 

que necesita recortar?

+0

Voy a experimentar e informar de nuevo. –

+3

Una cosa a tener en cuenta es que desempaquetar 'A' elimina los espacios finales 'de forma gratuita'. –

+0

Parece prometedor hasta el momento. El benchmarking básico muestra una serie de substrs por campo y expresiones regulares finales de banda es aproximadamente un 50% más rápido para nosotros que el uso de un desempaquetado. Las pruebas están pasando, pero mi pantalla está llena de advertencias de Perlish, así que todavía no estoy allí. –

7

En realidad, me he enfrentado a este problema una y otra vez. Unpack is better than substr.

En lo que respecta a los espacios de arrastre, estás bastante jodido. Ese cambio de expresiones regulares es la forma "oficial" de hacerlo. Es posible que pueda obtener cierta eficiencia refinando sus declaraciones de desempaquetado (suponiendo que no hay datos con más de 4 dígitos, ¿por qué desempaquetar los 12 dígitos completos del campo?), Pero de lo contrario, el análisis es solo una p.i.t.a.

Buena suerte con sus datos planas. Fricking legacy chatarra, cómo lo odio.

3

¿Estás seguro de que estás atado al procesador en esta tarea? El cálculo es lo suficientemente simple como para sospechar que todo el proceso es probable que esté vinculado a E/S. En cuyo caso, la optimización para desempaquetar más rápido no le permitirá ganar mucho tiempo.

En caso de que esté procesado, el problema tal como se describe parece bastante paralelable, pero, por supuesto, el diablo está en los detalles del cálculo de su empresa.

+0

Bastante seguro, sí. En el código real, las E/S y el desempaquetado están separados, a diferencia del ejemplo de maceta anterior. El paralelismo es una buena decisión, ya lo hacemos. Sin embargo, aún quisiera dedicar menos tiempo a cortar las cuerdas. –

+2

Para mí, el mayor cuello de botella es la propia base de datos; si está lidiando con datos planos, es posible que esté lidiando con una horrible base de datos plana antigua. Soluciono el problema sacando los datos a intervalos a una base de datos moderna, pero para datos de "tiempo real" casi siempre es la base de datos la que está causando las ralentizaciones. – Satanicpuppy

+0

Tengo que estar de acuerdo. En general, nuestro proceso consiste en pasar la mayor parte del tiempo extrayendo datos en la base de datos.Podemos usar procesos paralelos para acelerar esto. Me llamó la atención que como una gran parte del 'Perl-time' se gastó en una parte tan pequeña del código, podría haber un margen para una optimización fácil aquí. –

1

Esto también podría ser algo para XS, por lo que utiliza una función C para cambiar los datos. Podría imaginar que esto es mucho más rápido que cualquier otra cosa, ya que puedes controlar manualmente cuando realmente se copian datos.
El proceso de compilación será más difícil ya que tiene una dependencia del compilador de C y requerirá pasos de integración adicionales.

+0

Gracias. Probablemente para mi proyecto, la ganancia no justificaría la complejidad adicional. Como comentó Satanicpuppy, hay otras partes del proceso que son más caras que esta. Sin embargo, podría funcionar para otra persona con un problema similar. –

+3

Si la mayor parte del tiempo de CPU se gasta en el motor de expresiones regulares y una función incorporada, la mayor parte del tiempo se gasta en C land (es decir, no en optativas para caminar y manejo de estructuras de datos perl). Y se necesita un buen programador para vencer a las personas que escribieron la máquina virtual Perl. desempaquetar es rápido! – tsee

1

Simplemente haga que vaya en paralelo. Es trivial, y en cualquier máquina remotamente moderna será más rápido.

0

El punto de referencia de una versión basada en substrato de nuestro código sugiere que podría ser aproximadamente un 50% más rápido que nuestro desempaquetado existente. Comparando los códigos vigentes en la aplicación real, la versión substr nos dio una reducción del 16% en el tiempo de ejecución. Esto está cerca de lo que esperábamos basado en el punto de referencia y los perfiles a los que se hace referencia en la pregunta.

Esta optimización puede ser útil para nosotros. Pero, tenemos una migración a un nuevo sistema operativo en el horizonte, por lo que vamos a esperar y ver cómo funcionan los códigos allí antes de continuar. Hemos agregado una prueba para vigilar los puntos de referencia comparativos.

El idioma que tenemos ahora es:

$FIELDS::transaction_date = substr($_, 0, 8) || ''; 
$FIELDS::transaction_date =~ s/\s+\z//; 
$FIELDS::customer_id = substr($_, 8, 20) || ''; 
$FIELDS::customer_id =~ s/\s+\z//; 

seguido de la eliminación selectiva espacio inicial como antes.

Gracias por todas las respuestas hasta el momento. Aceptaré los de Sinan porque funcionó para nosotros, a pesar de que parecían 'simplemente erróneos'.

Cuestiones relacionadas