2010-07-28 21 views
6

Quiero obtener una matriz de bytes (Array [Byte]) desde algún lugar (leer desde el archivo, desde el socket, etc.) y luego proporcionar una forma eficiente de extraer bits de ella (por ejemplo, proporcionar una función para extraer un 32- bit entero desde el desplazamiento N en la matriz). Luego me gustaría envolver la matriz de bytes (que la oculta) proporcionando funciones para extraer bits de la matriz (probablemente usando vago val para cada bit para extraer).¿Cuál es la forma más eficiente de hacer matrices de bytes inmutables en Scala?

Me imagino tener una clase de envoltura que toma un tipo de matriz de bytes inmutables en el constructor para probar que el contenido de la matriz nunca se modifica. IndexedSeq [Byte] parece relevante, pero no pude encontrar la forma de pasar de Array [Byte] a IndexedSeq [Byte].

La parte 2 de la pregunta es si utilicé IndexedSeq [Byte] ¿el código resultante será más lento? Necesito que el código se ejecute lo más rápido posible, así que me quedaría con Array [Byte] si el compilador pudiera hacer un mejor trabajo con él.

Podría escribir una clase contenedora alrededor de la matriz, pero eso disminuiría la velocidad, un nivel adicional de indirección para cada acceso a bytes en la matriz. El rendimiento es crítico debido a la cantidad de accesos de matriz que se requerirán. Necesito un código rápido, pero me gustaría hacer el código muy bien al mismo tiempo. ¡Gracias!

PD: Soy un novato de Scala.

Respuesta

13

tratamiento Array[T] como IndexedSeq[T] no podría ser más simple:

Array(1: Byte): IndexedSeq[Byte] // trigger an Implicit View 
wrapByteArray(Array(1: Byte)) // explicitly calling 

Unboxing te va a matar mucho antes de que una capa extra de indirección.

C:\>scala -Xprint:erasure -e "{val a = Array(1: Byte); val b1: Byte = a(0); val 
b2 = (a: IndexedSeq[Byte])(0)}" 
[[syntax trees at end of erasure]]// Scala source: scalacmd5680604016099242427.s 
cala 

val a: Array[Byte] = scala.Array.apply((1: Byte), scala.this.Predef. 
wrapByteArray(Array[Byte]{})); 
val b1: Byte = a.apply(0); 
val b2: Byte = scala.Byte.unbox((scala.this.Predef.wrapByteArray(a): IndexedSeq).apply(0)); 

Para evitar esto, la biblioteca de colecciones Scala debe especializado en el tipo de elemento, en el mismo estilo que Tuple1 y Tuple2. Me dijeron que esto está planeado, pero es un poco más complicado que simplemente golpear @specialized en todas partes, así que no sé cuánto tiempo llevará.

ACTUALIZACIÓN

Sí, WrappedArray es mutable, aunque collection.IndexedSeq[Byte] no tiene métodos para mutar, por lo que podría clientes sólo confía en que no se ponga a una interfaz mutable. La próxima versión de Scalaz incluirá ImmutableArray, que previene esto.

El boxeo viene la recuperación de un elemento de la colección a través de este método genérico:

trait SeqLike[+A, +Repr] extends IterableLike[A, Repr] { self => 
    def apply(idx: Int): A 
} 

A nivel JVM, esta firma es del tipo que se ha borrado a:

def apply(idx: Int): Object 

Si su colección contiene primitivas , es decir, subtipos de AnyVal, deben estar enmarcados en el envoltorio correspondiente que se devolverá desde este método. Para algunas aplicaciones, esta es una gran preocupación de rendimiento. Se han escrito bibliotecas enteras en Java para evitar esto, notablemente fastutils.

Annotation directed specialization se agregó a Scala 2.8 para indicar al compilador que genere varias versiones de una clase o método adaptado a las permutaciones de tipos primitivos. Esto ya se aplicó en algunos lugares de la biblioteca estándar, p. TupleN, ProductN, Function{0, 1, 2}.Si esto también se aplicó a la jerarquía de colecciones, este costo de rendimiento podría ser aliviado.

+0

Lo siento, como novato de Scala no entiendo tu ejemplo. ¿Estás diciendo que usar Array [Byte] hará un montón de unboxing? Además, no estaba seguro de cómo activar la vista implícita. Ingenuamente agregué ": IndexedSeq [Byte]" después de la instanciación de la matriz, pero se quejó con "type mismatch". Además, wrapByteArray() parece devolver un IndexedSeq mutable [Byte], no la versión inmutable. –

11

Si quieres trabajar con secuencias en Scala, recomiendo que elija una de ellas:

SEQs inmutables:

(SEQs vinculados) Lista, corriente, cola

(indexados SEQs) Vector

SEQs mutables:

(SEC vinculados) ListBuffer

(SEC indexado) ArrayBuffer

La nueva (2.8) colecciones Scala han sido difíciles de entender para mí, debido principalmente a la falta de documentación (correcto), sino también por el código fuente (jerarquías complejas). Para aclarar mi mente Hice este dibujo para visualizar la estructura básica:

alt text http://www.programmera.net/scala/img/seq_tree.png

Además, tenga en cuenta que Array no es parte de la estructura de árbol, es un caso especial, ya que envuelve la matriz de Java (que es un caso especial en Java).

+3

En Scala 2.8, su 'Array' es para las matrices de Java, ya que' 'String'' de Scala es' 'String'' de Java, una y la misma, no incluida. Hay envoltorios con conversiones implícitas correspondientes que permiten que elementos como los HOF de Scala para secuencias se utilicen con estos tipos "prestados", pero el 'Array' básico (y' String') son las entidades de Java. –

+0

Gracias, pero ¿algún comentario sobre el rendimiento del enfoque? Es decir, si uso IndexedSeq [Byte] en mi código, ¿será tan rápido como Array [Byte] (byte [] en Java)? Noto que otra respuesta advierte acerca de los gastos generales de unboxing: ¿van a matar el rendimiento de Scala para tratar con matrices de bytes? –

+1

'Vector' (la única subclase instanciable de' IndexedSeq' distinta de las diversas clases 'Range' y' WrappedString') no está especializada (para ningún tipo de elemento) en Scala 2.8.0, por lo que pagará una gran cantidad de memoria sobrecarga, así como un costo de tiempo de acceso marginal, todo debido al boxeo y el desempaquetado, como dices. –

Cuestiones relacionadas