2009-08-26 4 views
7

Was ist der schnellste Weg, um einen String in ein byte [] Array in C# zu verwandeln? Ich sende Tonnen von String-Daten durch Sockets und muss jeden einzelnen Vorgang optimieren. Zur Zeit habe ich die Saite um byte [] Arrays Transformation vor dem Senden mit:Der schnellste Weg (performanceweise), einen String in ein Array byte [] in C# umzuwandeln

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

Möglicherweise möchten Sie die Anwendung profilieren, bevor Sie zu viel Zeit hier verbringen. Gut-Reaktionen sind, dass dies nicht wie ein Leistungsengpass klingt, aber es gibt keine Möglichkeit, ohne harte Zahlen zu erzählen. – Rob

+4

+1 für die Stimmung, aber das ist in den Engpass und jeder Nano zählt hier – Nosrama

+0

Ist der Flaschenhals die Menge der Daten, die Sie über die Leitung oder die Konvertierung senden? – kibibu

Antwort

14

Wenn alle Ihre Daten wirklich gehen ASCII sein, dann können Sie in der Lage sein, es zu tun, etwas schneller als ASCIIEncoding, die verschiedene (ganz vernünftig) Bits hat der Fehlerbehandlung usw. Sie können es auch beschleunigen, indem Sie nicht ständig neue Byte-Arrays erstellen. Angenommen, Sie sind eine obere Grenze haben, die alle Ihre Nachrichten werden unter:

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

Sie würden dann so etwas wie:

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); 

Das ist ziemlich verzweifelt Optimierung though.Ich würde bei ASCIIEncoding bleiben, bis ich nachgewiesen wäre, dass dies der Flaschenhals war (oder zumindest, dass diese Art von grotty Hack nicht hilft).

+5

+1 für * verzweifelt * –

+0

Ist der Inline-Cast (?) Operator "as" nicht schneller als C-Style Cast? d. h. (Zeichen [i] & 0x7f) als Byte. –

+1

@James Schek: Nur wenn es scheitert! ;-) Auch hier ist es unpassend, da es sich um eine tatsächliche Typumwandlung, keine Typüberprüfung handelt, * und * das 'as' Schlüsselwort kann nur für Typen verwendet werden, die' null' sein können (zB Referenztypen und Nullable '/' T? '). –

9

Ich würde sagen, wie Sie es jetzt tun viel gut ist. Wenn Sie sich wirklich mit der Optimierung auf sehr niedrigem Niveau befassen, ist die beste Empfehlung, die ich machen kann, Reflector zu bekommen. Mit Reflector können Sie sich den Code selbst anschauen (die meiste Zeit) und sehen, was die Algorithmen sind. Wenn reflector Sie nicht anzeigt, können Sie immer Microsofts SSCLI (Common Language Infrastructure für gemeinsam genutzte Quellen) herunterladen, um den C++ - Code hinter MethodImplOptions.InternalCall-Methoden anzuzeigen.

Als Referenz hier ist die tatsächliche Umsetzung von 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

stelle ich mir die GetBytes() Funktion bereits gut dafür optimiert ist. Ich kann mir keine Vorschläge vorstellen, um die Geschwindigkeit Ihres bestehenden Codes zu verbessern.

EDIT - Weißt du, ich weiß nicht, ob das schneller ist oder nicht. Aber hier ist eine andere Methode, um die BinaryFormatter mit:

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

Der Grund, warum ich denke, das könnte schneller ist, dass es die Codierung Schritt überspringt. Ich bin mir auch nicht ganz sicher, ob das richtig funktioniert. Aber du könntest es versuchen und sehen. Wenn Sie die ASCII-Codierung benötigen, hilft das natürlich nicht.

Ich hatte gerade einen anderen Gedanken. Ich glaube, dass dieser Code die doppelte Anzahl an Bytes zurückgeben würde als GetBytes mit ASCII-Codierung. Der Grund dafür ist, dass alle Strings in .NET Unicode hinter den Kulissen verwenden. Und natürlich verwendet Unicode 2 Bytes pro Zeichen, während ASCII nur 1 verwendet. Daher ist der BinaryFormatter in diesem Fall wahrscheinlich nicht das Richtige, da Sie die Menge der Daten, die Sie über den Socket senden, verdoppeln würden.

+0

Nur ein Hinweis zur Verwendung eines binären Formatierer und Speicherstream. Sie müssten diese beiden Objekte jedes Mal konstruieren, wenn Sie Bytes konvertieren müssten, wo Sie einfach mit dem ASCIIEncoder eine Methode aufrufen, und das ist alles. Die Objektkonstruktionskosten sind auf diesem niedrigen Niveau ziemlich hoch und könnten ein wichtiger Faktor sein. – jrista

+0

Ausgezeichneter Punkt. Dies könnte etwas sein, das Sie nur bei großen Zeichenketten berücksichtigen möchten, bei denen die Länge der Zeichenkette die Konstruktionskosten ausgleicht. Natürlich ist das alles theoretisch (zumindest für mich). Ich weiß nicht einmal, ob diese Methode jemals schneller sein würde. –

1

Worauf möchten Sie optimieren? ZENTRALPROZESSOR? Bandbreite?

Wenn Sie die Bandbreite optimieren möchten, können Sie versuchen, die Zeichenfolgedaten vorher zu komprimieren.

Zuerst profilieren Sie Ihren Code, herauszufinden, was die langsamen Bits sind, bevor Sie versuchen, auf einem so niedrigen Niveau zu optimieren.

+0

+1: Ja, ja, ja –

+0

Ich optimiere für CPU – Nosrama

+0

Sie sollten auch * Speicherbus * Bandbreite berücksichtigen. Bei der Ausführung von einfachen Rechenoperationen mit großen Datenmengen ist es oft der Fall, dass die CPU die meiste Zeit damit verbringt, auf den viel langsameren Takt des FSB zu warten. – Crashworks

0

Wie andere bereits gesagt haben, ist die Codierungsklasse bereits für diese Aufgabe optimiert, daher wird es wahrscheinlich schwierig sein, sie schneller zu machen. Es gibt eine Mikro-Optimierung, die Sie tun könnten: Verwenden Sie statt new ASCIIEncoding(). Aber wie jeder weiß, Mikro-Optimierungen sind schlecht;)

1

Ohne weitere Hinweise auf Ihre Nebenläufigkeitsanforderungen (oder irgendetwas anderes): Können Sie im ThreadPool einige Threads erzeugen, die die Zeichenfolgen in Bytearrays konvertieren und sie in eine Warteschlange ablegen und ein weiteres Thread die Warteschlange überwachen und senden Daten?

0

Ich würde vorschlagen, Profiling, was Sie tun. Ich finde es zweifelhaft, dass die Geschwindigkeit der Umwandlung einer Zeichenfolge in ein Byte-Array ein größeres Problem im Durchsatz als die Geschwindigkeit des Sockets selbst ist.

+0

In den Kommentaren erklärt er, dass er es profiliert und den Engpass hier verfolgt hat. – Crashworks

0

Nur ein weiterer Tipp: Ich weiß nicht, wie Sie Ihre ersten Strings erstellen, aber denken Sie daran, dass StringBuilder.Append ("etwas") ist wirklich schneller als etwas wie myString + = "etwas".

In dem ganzen Prozess des Erstellens der Zeichenfolgen und des Sendens von ihnen über eine Socket-Verbindung wäre ich überrascht, wenn der Flaschenhals die Konvertierung von Strings in Bytearrays wäre. Aber ich bin sehr interessiert, wenn jemand das mit einem Profiler testen würde.

Ben