2009-08-26 26 views
7

¿Cuál es la forma más rápida de convertir una cadena en una matriz de bytes [] en C#? Estoy enviando toneladas de datos de cadena a través de sockets y necesito optimizar cada operación. Actualmente transformo en las cadenas de byte [] matrices antes de enviar usando:Forma más rápida (rendimiento) de convertir una cadena en una matriz de bytes [] en C#

private static readonly Encoding encoding = new ASCIIEncoding(); 
//... 
byte[] bytes = encoding.GetBytes(someString); 
socket.Send(bytes); 
//... 
+4

Puede que quiera hacer un perfil de la aplicación antes de pasar demasiado tiempo aquí. Las reacciones intestinales son que esto no suena como un cuello de botella de rendimiento, pero no hay forma de saber sin números duros. – Rob

+4

+1 por el sentimiento, pero esto está en el cuello de botella y cada nano cuenta aquí – Nosrama

+0

¿El cuello de botella es la cantidad de datos que está enviando por el cable o la conversión? – kibibu

Respuesta

14

Si todos sus datos están realmente va a ser ASCII, entonces usted puede ser capaz de hacerlo un poco más rápido que ASCIIEncoding, que tiene varios bits (totalmente razonable) de manejo de errores, etc. También puede acelerarlo evitando crear nuevas matrices de bytes todo el tiempo. Asumiendo que tiene un límite superior, que todos sus mensajes estarán bajo:

void QuickAndDirtyAsciiEncode(string chars, byte[] buffer) 
{ 
    int length = chars.Length; 
    for (int i = 0; i < length; i++) 
    { 
     buffer[i] = (byte) (chars[i] & 0x7f); 
    } 
} 

Se podría entonces hacer algo como:

readonly byte[] Buffer = new byte[8192]; // Reuse this repeatedly 
... 
QuickAndDirtyAsciiEncode(text, Buffer); 
// We know ASCII takes one byte per character 
socket.Send(Buffer, text.Length, SocketFlags.None); 

Esta es la optimización bastante desesperado sin embargo.Me quedaría con ASCIIEncoding hasta que hubiera demostrado que este era el cuello de botella (o al menos que este tipo de hack grotty no ayuda).

+5

+1 para * desesperado * –

+0

¿No es el operador de lanzamiento en línea (?) "Como" más rápido que el lanzamiento de estilo C? es decir, (caracteres [i] y 0x7f) como byte. –

+1

@James Schek: ¡Solo si falla! ;-) Además, es inapropiado aquí, ya que esta es una conversión de tipo real, no una verificación de tipo, * y * la palabra clave 'as' solo se puede usar para tipos que pueden ser' null' (es decir, tipos de referencia y 'Nullable '/' T? '). –

9

Yo diría que la forma en que está haciendo ahora es un montón buena. Si realmente le preocupa la optimización de muy bajo nivel, la mejor recomendación que puedo hacer es obtener Reflector. Con el reflector, puede ver el código usted mismo (la mayoría del tiempo) y ver cuáles son los algoritmos. Si el reflector no te muestra, siempre puedes descargar Microsofts SSCLI (Infraestructura de Lenguaje Común de Fuente Compartida) para ver el código C++ detrás de los métodos MethodImplOptions.InternalCall.

Para referencia, aquí es la aplicación real de Encoding.ASCII.GetBytes:

public override int GetBytes(string chars, int charIndex, int charCount, byte[] bytes, int byteIndex) 
{ 
    if ((chars == null) || (bytes == null)) 
    { 
     throw new ArgumentNullException(); 
    } 
    if ((charIndex < 0) || (charCount < 0)) 
    { 
     throw new ArgumentOutOfRangeException(); 
    } 
    if ((chars.Length - charIndex) < charCount) 
    { 
     throw new ArgumentOutOfRangeException(); 
    } 
    if ((byteIndex < 0) || (byteIndex > bytes.Length)) 
    { 
     throw new ArgumentOutOfRangeException(); 
    } 
    if ((bytes.Length - byteIndex) < charCount) 
    { 
     throw new ArgumentException(); 
    } 
    int num = charIndex + charCount; 
    while (charIndex < num) 
    { 
     char ch = chars[charIndex++]; 
     if (ch >= '\x0080') 
     { 
      ch = '?'; 
     } 
     bytes[byteIndex++] = (byte) ch; 
    } 
    return charCount; 
} 
1

Me imagino que el GetBytes función() ya está bien optimizado para esto. No puedo pensar en ninguna sugerencia para mejorar la velocidad de tu código existente.

EDITAR - Ya sabes, no sé si esto es más rápido o no. Pero aquí hay otro método que utiliza el BinaryFormatter:

BinaryFormatter bf = new BinaryFormatter(); 
MemoryStream ms = new MemoryStream(); 
bf.Serialize(ms, someString); 
byte[] bytes = ms.ToArray(); 
ms.Close(); 
socket.Send(bytes); 

La razón creo que esto podría ser más rápido es que se salta la etapa de codificación. Tampoco estoy completamente seguro de que esto funcione correctamente. Pero puedes intentarlo y ver. Por supuesto, si necesita la codificación ASCII, esto no ayudará.

Solo tuve otro pensamiento. Creo que este código devolvería el doble de bytes que el uso de GetBytes con codificación ASCII. La razón es que todas las cadenas en .NET usan unicode detrás de escena. Y, por supuesto, Unicode usa 2 bytes por carácter, mientras que ASCII usa solo 1. Por lo tanto, es probable que BinaryFormatter no sea lo adecuado en este caso porque duplicaría la cantidad de datos que está enviando por el socket.

+0

Solo una nota sobre el uso de un formateador binario y una secuencia de memoria. Tendría que construir esos dos objetos cada vez que necesitara convertir bytes, donde como con el ASCIIEncoder, llama a un método y eso es todo. El costo de construcción del objeto es bastante alto en este nivel bajo, y podría ser un factor importante. – jrista

+0

Excelente punto. Esto puede ser algo que solo desearía considerar con cadenas grandes, donde la longitud de la cadena compensa el costo de construcción. Por supuesto, esto es todo teórico (al menos para mí). Ni siquiera sé si este método será más rápido. –

1

¿Para qué estás tratando de optimizar? ¿UPC? Ancho de banda?

Si desea optimizar el ancho de banda, puede intentar comprimir los datos de cadena de antemano.

Primero, perfile su código, descubra qué son las partes lentas, antes de intentar optimizar a un nivel tan bajo.

+0

+1: Sí, sí, sí –

+0

Estoy optimizando para CPU – Nosrama

+0

También debería considerar * bus de memoria * ancho de banda. Cuando se realizan operaciones computacionalmente simples en grandes cantidades de datos, a menudo la CPU pasa la mayor parte del tiempo esperando el reloj mucho más lento del FSB. – Crashworks

0

Como han dicho otros, la clase de Codificación ya está optimizada para esa tarea, por lo que probablemente será difícil hacerlo más rápido. Hay una micro optimización que podría hacer: use en lugar de new ASCIIEncoding(). Pero, como todo el mundo sabe, los micro-optimizaciones son malos;)

1

Sin tener en cuenta sus requisitos de concurrencia (ni nada): puede generar algunos hilos en ThreadPool que conviertan las cadenas en matrices de bytes y colocarlas en una cola, y tener un hilo más viendo la cola y enviando el ¿datos?

0

Te sugiero que perfile lo que estás haciendo. Encuentro dudoso que la velocidad de conversión de una cadena a una matriz de bytes sea un problema mayor en el rendimiento que la velocidad del socket mismo.

+0

En los comentarios, explica que lo ha perfilado y ha rastreado el cuello de botella aquí. – Crashworks

0

Solo otro consejo: no sé cómo se crean las cadenas iniciales, pero recuerde que StringBuilder.Append ("algo") es realmente más rápido que algo como myString + = "algo".

En todo el proceso de creación de las cadenas, y enviándolas a través de una conexión de socket, me sorprendería si el cuello de botella fuera la conversión de cadenas en matrices de bytes. Pero estoy muy interesado si alguien lo prueba con un generador de perfiles.

Ben

Cuestiones relacionadas