haskell io performance bytestring

haskell - ¿Cuándo uso ByteString y cuándo no?



io performance (2)

La entrada masiva suele ser más rápida con las secuencias de byte, ya que los datos son densos, simplemente hay menos datos que se barajan del disco en la memoria.

Escribir datos como salida , sin embargo, es un poco diferente. Normalmente, estás serializando una estructura, generando muchas escrituras pequeñas. Por lo tanto, las escrituras densas y masivas de bytestrings no ayudan mucho en ese caso. Incluso las Strings normales funcionarán de manera razonable en una salida incremental.

Sin embargo, no todo está perdido. Podemos recuperar escrituras masivas rápidas mediante la creación eficiente de bytestrings en la memoria. Este enfoque es tomado por los diversos paquetes *-builder :

En lugar de convertir los valores en muchos bytestrings pequeños y escribirlos uno por uno, transmitimos la conversión a un búfer en constante crecimiento y, a su vez, escribimos ese búfer en una gran pieza. Esto resulta en mucho menos sobrecarga de IO y mejoras de rendimiento (a menudo significativas) sobre la IO de cadena.

Este tipo de enfoque lo adoptan, por ejemplo, los servidores web en Haskell, o el eficiente sistema HTML, blaze .

Además, el rendimiento, incluso con escrituras masivas, dependerá de la eficiencia de cualquier función de conversión que tenga entre sus tipos y bytestrings. Para Integer , podría simplemente estar copiando el patrón de bits en la memoria para enviar, o en su lugar, pasar por un decodificador ineficiente. Como resultado, a veces tiene que pensar un poco sobre la calidad de la función de codificación que está utilizando, y no solo si debe usar Char / String o Bytestring IO.

He estado haciendo intentos bastante pobres en el problema PRIME1 en SPOJ. Descubrí que el uso de ByteString realmente ayudó al rendimiento para leer el texto del problema. Sin embargo, usar ByteString para escribir los resultados es en realidad un poco más lento que usar las funciones de Prelude. Estoy tratando de averiguar si lo estoy haciendo mal, o si esto se espera.

He realizado el perfilado y el cronometraje utilizando (putStrLn.show) y los equivalentes de ByteString de tres maneras diferentes:

  1. Pruebo a cada candidato para ver si es primo. Si es así, lo agrego a una lista y lo escribo con (putStrLn. Show)
  2. Hago una lista de todos los números primos y escribo la lista usando (putStrLn. Unlines. Show)
  3. Hago una lista de todos los números primos y escribo la lista usando map (putStrLn. Show)

Esperaba que los números 2 y 3 funcionaran más lentamente, ya que está creando una lista en una función y consumiéndola en otra. Al imprimir los números a medida que los genero, evito asignar cualquier memoria para la lista. Por otro lado, está realizando una llamada al sistema de llamadas con cada llamada a putStrLn. ¿Derecha? Así que lo probé y el # 1 fue, de hecho, el más rápido.

El mejor rendimiento se logró con la opción # 1 y las funciones Prelude ([Char]). Esperaba que mi mejor rendimiento fuera la opción # 1 con ByteString, pero este no era el caso. Sólo usé ByteStrings perezosos, pero no pensé que esto importaría. ¿Verdad?

Algunas preguntas:

  • ¿esperaría que los ByteStrings funcionen mejor para escribir un grupo de enteros en stdout?
  • ¿Me falta un patrón de manera de generar y escribir las respuestas que conduzcan a un mejor rendimiento?
  • Si solo estoy escribiendo números como texto, ¿cuándo, si alguna vez, hay un beneficio al usar ByteString?

Mi hipótesis de trabajo es que escribir Integer con ByteString es más lento si no los estás combinando con otro texto. Si está combinando enteros con [Char], entonces obtendría un mejor rendimiento al trabajar con ByteStrings. Es decir, la reescritura de ByteString de:

putStrLn $ "the answer is: " ++ (show value)

Será mucho más rápido que la versión escrita arriba. ¿Es esto cierto?

¡Gracias por leer!


Tenga en cuenta que el rendimiento no es la principal diferencia entre ByteString y String . El primero es para datos binarios, mientras que el segundo es para texto Unicode. Si tiene datos binarios, use ByteString , si tiene texto Unicode, use el tipo de texto del paquete de texto .