numeros leer lectura escritura escribir como codigo binarios binario archivos aprender alfabeto c# file-io binary

c# - lectura - como leer codigo binario



La forma más rápida de leer y escribir en binario (4)

Deberías hacer un perfil sobre tu código para revelar si este es el cuello de botella. También mirando su código, parece que está utilizando las llamadas de función .Net para escribir un byte en una matriz no administrada, que implica un pin en la memoria y una llamada al código inseguro ...

Es posible que sea mucho mejor declarar un .Net System.IO.MemoryStream y buscar y escribir en él, siempre que sea posible, utilizando un escritor de secuencias para enviar sus cambios, que debe usar menos llamadas de función y no requerirá un código inseguro. Encontrará las cosas del puntero mucho más útiles en C # si está haciendo cosas como DSP, donde necesita realizar una sola operación para cada valor en una matriz, etc.

EDITAR: Permítanme también mencionar que dependiendo de lo que estén haciendo, es posible que el almacenamiento en caché de la CPU entre en vigencia, si pueden seguir trabajando en un área pequeña de memoria que se ajusta a la caché, terminarán con la mejor actuación.

Actualmente estoy optimizando una aplicación, una de las operaciones que se realiza con mucha frecuencia es leer y escribir en formato binario. Necesito 2 tipos de funciones:

Set(byte[] target, int index, int value); int Get(byte[] source, int index);

Estas funciones son necesarias para firmas cortas y sin firmas, int y largas en orden grande y poco endian.

Aquí hay algunos ejemplos que he hecho, pero necesito una evaluación sobre las ventajas y desventajas:

el primer método es usar Marshal para escribir el valor en la memoria del byte [], el segundo está usando punteros simples para lograr esto y el tercero usa BitConverter y BlockCopy para hacer esto.

unsafe void Set(byte[] target, int index, int value) { fixed (byte* p = &target[0]) { Marshal.WriteInt32(new IntPtr(p), index, value); } } unsafe void Set(byte[] target, int index, int value) { int* p = &value; for (int i = 0; i < 4; i++) { target[offset + i] = *((byte*)p + i); } } void Set(byte[] target, int index, int value) { byte[] data = BitConverter.GetBytes(value); Buffer.BlockCopy(data, 0, target, index, data.Length); }

Y aquí están los métodos Read / Get:

el primero está usando Marshal para leer el valor del byte [], el segundo está usando punteros simples y el tercero está utilizando BitConverter nuevamente:

unsafe int Get(byte[] source, int index) { fixed (byte* p = &source[0]) { return Marshal.ReadInt32(new IntPtr(p), index); } } unsafe int Get(byte[] source, int index) { fixed (byte* p = &source[0]) { return *(int*)(p + index); } } unsafe int Get(byte[] source, int index) { return BitConverter.ToInt32(source, index); }

la verificación de límites debe hacerse, pero todavía no es parte de mi pregunta ...

Me agradaría que alguien pudiera decir cuál sería la mejor y más rápida manera en este caso o darme algunas otras soluciones para trabajar. Una solución genérica sería preferible

Acabo de hacer algunas pruebas de rendimiento, aquí están los resultados:

Set Marshal: 45 ms, Set Pointer: 48 ms, Set BitConverter: 71 ms Obtener Marshal: 45 ms, obtener puntero: 26 ms, obtener BitConverter: 30 ms

parece que usar punteros es la manera más rápida, pero creo que Marshal y BitConverter hacen algunas comprobaciones internas ... ¿alguien puede verificar esto?


Los indicadores son el camino a seguir. Pinchar objetos con la palabra clave fija es extremadamente económico, y evita la sobrecarga de funciones de llamada como WriteInt32 y BlockCopy. Para una "solución genérica" ​​puede simplemente usar void * y usar su propia memcpy (ya que se trata de pequeñas cantidades de datos). Sin embargo, los punteros no funcionan con verdaderos genéricos.


Utilizando Set1 de Set4 Gravell para Set4 y Set5 continuación, obtengo los siguientes números en mi máquina:

Set1: 197ms Set2: 102ms Set3: 604ms Set4: 68ms Set5: 55ms <==== pointer magic ;-p

Código:

unsafe static void Set5(byte[] target, int index, int value) { fixed (byte* p = &target[index]) { *((int*)p) = value; } }

Por supuesto, se vuelve mucho más rápido cuando la matriz de bytes no está anclada en cada iteración, sino solo una vez:

Set6: 10ms (little endian) Set7: 85ms (big endian)

Código:

if (!BitConverter.IsLittleEndian) { throw new NotSupportedException(); } watch = Stopwatch.StartNew(); fixed (byte* p = buffer) { for (int i = 0; i < LOOP; i++) { *((int*)(p + INDEX)) = VALUE; } } watch.Stop(); Console.WriteLine("Set6: " + watch.ElapsedMilliseconds + "ms"); watch = Stopwatch.StartNew(); fixed (byte* p = buffer) { for (int i = 0; i < LOOP; i++) { *((int*)(p + INDEX)) = System.Net.IPAddress.HostToNetworkOrder(VALUE); } } watch.Stop(); Console.WriteLine("Set7: " + watch.ElapsedMilliseconds + "ms");


Importante: si solo necesita el endian, consulte la magia del puntero por wj32 / dtb

Personalmente, estaría escribiendo directamente en un Stream (quizás con algo de almacenamiento en búfer), y reutilizando un búfer compartido que generalmente puedo asumir que está limpio. Luego puede hacer algunos accesos directos y asumir el índice 0/1/2/3.

Ciertamente, no use BitConverter , ya que no se puede utilizar para little / big-endian, lo que necesita. También me inclinaría a utilizar el cambio de bit en lugar de inseguro, etc. Es el más rápido, basado en lo siguiente (así que estoy contento de que así es como ya lo hago con mi código aquí , busque EncodeInt32Fixed ):

Set1: 371ms Set2: 171ms Set3: 993ms Set4: 91ms <==== bit-shifting ;-p

código:

using System; using System.Diagnostics; using System.Runtime.InteropServices; static class Program { static void Main() { const int LOOP = 10000000, INDEX = 100, VALUE = 512; byte[] buffer = new byte[1024]; Stopwatch watch; watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { Set1(buffer, INDEX, VALUE); } watch.Stop(); Console.WriteLine("Set1: " + watch.ElapsedMilliseconds + "ms"); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { Set2(buffer, INDEX, VALUE); } watch.Stop(); Console.WriteLine("Set2: " + watch.ElapsedMilliseconds + "ms"); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { Set3(buffer, INDEX, VALUE); } watch.Stop(); Console.WriteLine("Set3: " + watch.ElapsedMilliseconds + "ms"); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { Set4(buffer, INDEX, VALUE); } watch.Stop(); Console.WriteLine("Set4: " + watch.ElapsedMilliseconds + "ms"); Console.WriteLine("done"); Console.ReadLine(); } unsafe static void Set1(byte[] target, int index, int value) { fixed (byte* p = &target[0]) { Marshal.WriteInt32(new IntPtr(p), index, value); } } unsafe static void Set2(byte[] target, int index, int value) { int* p = &value; for (int i = 0; i < 4; i++) { target[index + i] = *((byte*)p + i); } } static void Set3(byte[] target, int index, int value) { byte[] data = BitConverter.GetBytes(value); Buffer.BlockCopy(data, 0, target, index, data.Length); } static void Set4(byte[] target, int index, int value) { target[index++] = (byte)value; target[index++] = (byte)(value >> 8); target[index++] = (byte)(value >> 16); target[index] = (byte)(value >> 24); } }