Raku: ¿hay una manera SUPER rápida de convertir una matriz en una cadena sin los espacios que separan los elementos?

8

Necesito convertir miles de cadenas de bytes binarios, cada una de aproximadamente un megabyte de longitud, en cadenas ASC. Esto es lo que he estado haciendo, y parece demasiado lento:

sub fileToCorrectUTF8Str ($fileName) { # binary file
    my $finalString = "";
    my $fileBuf = slurp($fileName, :bin);    
    for @$fileBuf { $finalString = $finalString ~ $_.chr; };    
    return $finalString;
}

~ @ b convierte @b en una cadena con todos los elementos separados por espacio, pero esto no es lo que quiero. Si @b = <abcd>; el ~ @ b es "abc d"; pero solo quiero "abcd", y quiero hacerlo REALMENTE rápido.

Entonces, ¿cuál es la mejor manera? Realmente no puedo usar hiper para paralelismo porque la cadena final se construye secuencialmente. O puedo?

lisprogtor
fuente

Respuestas:

10

TL; DR En un viejo rakudo, .decodees aproximadamente 100 veces más rápido.

En forma más larga para que coincida con su código:

sub fileToCorrectUTF8Str ($fileName) { # binary file
  slurp($fileName, :bin).decode
}

Notas de rendimiento

Primero, esto es lo que escribí para probar:

# Create million and 1 bytes long file:
spurt 'foo', "1234\n6789\n" x 1e5 ~ 'Z', :bin;

# (`say` the last character to check work is done)
say .decode.substr(1e6) with slurp 'foo', :bin;

# fileToCorrectUTF8Str 'foo' );

say now - INIT now;

En el rakudo de 2018.12TIO.run, lo anterior .decodepesa aproximadamente .05segundos por millón de archivos de bytes en lugar de aproximadamente 5segundos para su solución.

Por supuesto, podría / debería probar en su sistema y / o usar versiones posteriores de rakudo. Esperaría que la diferencia permanezca en el mismo orden, pero que los tiempos absolutos mejoren notablemente a medida que pasan los años.[1]

¿Por qué es 100 veces más rápido?

Bueno, en primer lugar, @en un Buf/ Blobexplícitamente fuerzas Raku para ver el antiguo artículo unitario ( una memoria intermedia) como plural cosa (una lista de elementos aka de varios artículos s ). Eso significa iteración de alto nivel que, para un búfer de un millón de elementos, es inmediatamente un millón de iteraciones / operaciones de alto nivel en lugar de una sola operación de alto nivel.

En segundo lugar, el uso .decodeno solo evita la iteración sino que también incurre en una sobrecarga de llamadas al método relativamente lenta una vez por archivo, mientras que al iterar hay potencialmente un millón de .chrllamadas por archivo. Las llamadas a métodos están (al menos semánticamente) enlazadas tarde, lo que en principio es relativamente costoso en comparación con, por ejemplo, llamar a un sub en lugar de un método (los subs generalmente están enlazados anticipadamente ).

Todo eso dijo:

  • Recuerde Caveat Empty [1] . Por ejemplo, las clases estándar de rakudo generan cachés de métodos, y es posible que el compilador simplemente alinee el método de todos modos, por lo que es posible que haya una sobrecarga insignificante para el aspecto de la llamada al método.

  • Consulte también la página de rendimiento del documento , especialmente Usar código de alto rendimiento existente .

¿Es el Buf.Strmensaje de error LTA ?

Actualización Ver el comentario de Liz ++.

Si intenta usar .Strun Bufo Blob(o equivalente, como usar el ~prefijo) obtendrá una excepción. Actualmente el mensaje es:

Cannot use a Buf as a string, but you called the Str method on it

El documento para .Stren Buf/Blob actualmente dice:

Para convertir a un Str necesitas usar .decode.

Podría decirse que es LTA que el mensaje de error no sugiere lo mismo.

Por otra parte, antes de decidir qué hacer con respecto a esto, en todo caso, debemos considerar qué y cómo la gente podría aprender de cualquier cosa que salga mal, incluidas las señales al respecto, como los mensajes de error, y también qué y cómo lo hacen en Actualmente aprendemos y sesgamos nuestras reacciones hacia la construcción de la cultura y la infraestructura adecuadas .

En particular, si la gente puede conectarse fácilmente entre un mensaje de error que ven y una discusión en línea que lo desarrolla, eso debe tenerse en cuenta y tal vez alentarse y / o facilitarse.

Por ejemplo, ahora hay un SO que cubre este problema con el mensaje de error, por lo que es probable que un google encuentre a alguien aquí. Apoyarse en eso bien podría ser un camino más apropiado hacia adelante que cambiar el mensaje de error. O tal vez no. El cambio sería fácil ...

Considere comentar a continuación y / o buscar problemas de rakudo existentes para ver si Buf.Strse está considerando la mejora del mensaje de error y / o si desea abrir un problema para proponer que se modifique. Cada roca que se mueve es al menos un gran ejercicio y, a medida que nuestro esfuerzo colectivo se vuelve cada vez más sabio, mejora (nuestra visión de) la montaña .

Notas al pie

[1] Como dice el conocido refrán latino Caveat Empty , el rendimiento tanto absoluto como relativo de cualquier característica de raku en particular, y más generalmente de cualquier código en particular, siempre está sujeto a variación debido a factores que incluyen las capacidades del sistema, su carga durante el tiempo que es ejecutando el código, y cualquier optimización realizada por el compilador. Así, por ejemplo, si su sistema está "vacío", entonces su código puede ejecutarse más rápido. O, como otro ejemplo, si espera un año o tres para que el compilador se acelere , los avances en el rendimiento de rakudo continúan siendo prometedores .

raiph
fuente
2
Hay un par de formas en que puede optimizar aún más esto. slurpes solo un contenedor para IO::Path.slurpen este caso, por lo que llamar en su .IO.slurplugar hace que sea aproximadamente un 2% más rápido en mis puntos de referencia. Si extrae el archivo :enc<latin1>, terminará con un búfer de caracteres como lo haría con la codificación UTF-8 predeterminada, pero omita las comprobaciones de si el archivo extraído es realmente UTF-8 válido que está haciendo al decodificar a una cadena UTF-8 de todos modos, lo que hace que sea aproximadamente un 10% más rápido también.
Kaiepi
Gracias por toda tu ayuda !!! Con .decode y otros cambios menores de código, reduzco unos 8 segundos por cada cadena larga que tengo que procesar. Gracias !!!
lisprogtor
Hola @lisprogtor La metáfora "afeitarse" significa "reducir un número en una cantidad muy pequeña" . ¿Cuál fue su tiempo antes de reducirlo a unos 8 segundos por cadena larga? ¿Es suficiente la reducción?
raiph
¡8 segundos es definitivamente mejor que "afeitarse"! Gracias !!!
lisprogtor