2011-05-11 25 views
23

He estado haciendo intentos bastante pobres en el problema PRIME1 en SPOJ. Descubrí que usar ByteString realmente ayudó a rendimiento para leer en 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 Preludio. Estoy tratando de averiguar si lo estoy haciendo mal, o si esto es lo que se espera.¿Cuándo uso ByteString y cuándo no?

He realizado perfilado y el tiempo usando (putStrLn.show) y el ByteString equivalentes de tres maneras diferentes:

  1. I prueba cada candidato para ver si es primo. Si es así, lo agrego a una lista y escribo con (putStrLn. espectáculo)
  2. que hacer una lista de todos los números primos y escribir la lista mediante (putStrLn. Unlines espectáculo.)
  3. I hacer una lista de todos los números primos y escribir la lista mediante mapa (putStrLn. espectáculo)

que esperaba los números 2 y 3 para realizar más lenta a medida que está construyendo una lista en una función y consumirlo en otro. Al imprimir los números a medida que los genero, evito asignar memoria a la lista. Por otro lado, está realizando una llamada al sistema de llamadas con cada llamada a putStrLn. ¿Derecha? Así que probé y el n. ° 1 fue, de hecho, el más rápido.

El mejor rendimiento se logró con la opción # 1 y las funciones Preludio ([Char]). Esperaba que mi mejor rendimiento fuera la opción n. ° 1 con ByteString, pero este no era el caso. Solo usé ByteStrings perezoso, pero no pensé que esto fuera importante. ¿Verdad?

Algunas preguntas:

  • se puede esperar a los ByteString funcionan mejor para escribir un montón de enteros en la salida estándar?
  • ¿Me falta un patrón de forma a generar y escribir las respuestas que conducirían a un mejor rendimiento de ?
  • Si solo estoy escribiendo números como texto , ¿cuándo, si alguna vez, hay un beneficio de al usar ByteString?

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

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

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

¡Gracias por leer!

Respuesta

18

Hacer la entrada a granel es generalmente más rápido con las cadenas de bytes, ya que los datos son densos, simplemente hay menos datos para mezclar desde el disco a la memoria.

Escribir datos como salida sin embargo, es un poco diferente.Normalmente, está serializando una estructura, generando muchas escrituras pequeñas. Por lo tanto, las escrituras densas y masivas de cadenas de bytes no le ayudan mucho en ese caso. Incluso el Strings normal funcionará de forma razonable con un rendimiento incremental.

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

En lugar de convertir los valores a un montón de pequeñas cadenas de bytes, y escribiéndolas uno a la vez, transmitir los conversión en un búfer en constante crecimiento, y a su vez, escribir ese búfer en una gran pieza. Esto da como resultado mucho menos sobrecarga de E/S, y mejoras de rendimiento (a menudo significativas) sobre E/S de cadena.

Este tipo de enfoque lo toma, p. servidores web en Haskell, o el eficiente sistema HTML, blaze.

Además, el rendimiento, incluso con las grabaciones masivas, dependerá de la eficiencia de cualquier función de conversión que tenga entre sus cadenas de tipo y de bytes. Para Integer, podría simplemente copiar el patrón de bits en la memoria a la salida 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 usar Char/String o bytes Iing.

+0

¿Puede indicarme las agallas de una de las bibliotecas mencionadas anteriormente? Es decir, ¿la sección que transmite la conversión a un buffer en constante crecimiento? No estoy familiarizado con las partes internas de ninguna de esas bibliotecas. ¿Haría renderHtml en Text.Blaze.Renderer.UTF8 ser un ejemplo de esto? –

+0

P.E. en binario un [generador] (http://hackage.haskell.org/packages/archive/binary/0.5.0.2/doc/html/src/Data-Binary-Builder.html#Builder) es una función de continuación que actualiza un buffer mutable, asincrónicamente. –

+0

Parece que tengo mi tarea de lectura por la noche. ¡Gracias! –

7

Tenga en cuenta que el rendimiento no es la principal diferencia entre ByteString y String. El primero es para datos binarios, mientras que el último es para texto Unicode. Si tiene datos binarios, use ByteString, si tiene texto Unicode, use el tipo Text del text package.