2009-08-26 8 views
7

Jaki jest najszybszy sposób przekształcenia łańcucha znaków w tablicę bajtów [] w języku C#? Wysyłam tony danych ciągów przez gniazda i muszę zoptymalizować każdą operację. Obecnie przekształcić struny do byte [] tablic przed wysłaniem za pomocą:Najszybszy sposób (pod względem wydajności), aby przekształcić ciąg w tablicę bajtów [] w C#

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

Może zechcieć profilować aplikację, zanim spędzicie tu zbyt dużo czasu. Odruchy jelita polegają na tym, że nie brzmi to jak wąskie gardło wydajności, ale nie da się odróżnić bez twardych liczb. – Rob

+4

+1 za sentyment, ale to jest w wąskim gardle i liczy się każda nano. – Nosrama

+0

Czy wąskie gardło to ilość danych wysyłanych przez kabel lub konwersję? – kibibu

Odpowiedz

14

Jeżeli wszystkie dane są naprawdę będzie ASCII, to może być w stanie zrobić to nieco szybciej niż ASCIIEncoding, który ma różne (całkowicie uzasadnione) bitów obsługi błędów itp. Możesz także być w stanie go przyspieszyć, unikając tworzenia nowych tablic bajtów przez cały czas. Zakładając, że górną granicę którym wszystkie wiadomości będą pod:

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

Można by wtedy zrobić coś takiego:

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

Jest to dość rozpaczliwa optymalizacja chociaż.Trzymałbym się z ASCIIEncoding, dopóki nie dowiedziałbym się, że sprawdził, że jest to wąskie gardło (a przynajmniej to, że ten rodzaj hackowania nie pomaga).

+5

+1 za * zdesperowany * –

+0

Czy operator rzutujący (?) Nie jest "szybszy" od obsady w stylu C? tj. (chars [i] i 0x7f) jako bajt. –

+1

@James Schek: Tylko w przypadku niepowodzenia! ;-) Ponadto, jest to niewłaściwe tutaj, ponieważ jest to faktyczna konwersja typu, a nie kontrola typu, * i * słowo kluczowe 'as' może być używane tylko dla typów, które mogą mieć wartość' null' (tj. Typy odniesienia i 'Nullable '/' T? '). –

9

Powiedziałbym, że jak robisz to teraz jest dużo dobra. Jeśli naprawdę zależy Ci na bardzo niskim poziomie optymalizacji, najlepszą rekomendacją, jaką mogę zrobić, jest uzyskanie Reflectora. Dzięki reflektorowi możesz samemu spojrzeć na kod (przez większość czasu) i zobaczyć, jakie są algorytmy. Jeśli reflektor Cię nie pokazuje, zawsze możesz pobrać MicrosoftCLIC SSCLI (Shared Language Common Language Infrastructure), aby zobaczyć kod C++ za metodami MethodImplOptions.InternalCall.

Dla porównania, tutaj jest rzeczywista realizacja 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

sobie wyobrazić getBytes function() jest już dobrze zoptymalizowane dla tego produktu. Nie mogę wymyślić żadnych sugestii, aby poprawić prędkość twojego istniejącego kodu.

EDYCJA - Wiesz, nie wiem, czy to jest szybsze, czy nie. Ale tu jest inna metoda pomocą BinaryFormatter:

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

Powodem myślę, że to może szybciej jest to, że pomija się etap kodowania. Nie jestem też całkowicie pewien, czy to zadziała poprawnie. Ale możesz spróbować i zobaczyć. Oczywiście, jeśli potrzebujesz kodowania ASCII, to to nie pomoże.

Właśnie miałem inną myśl. Wierzę, że ten kod zwróciłby podwójną liczbę bajtów niż przy użyciu GetBytes z kodowaniem ASCII. Powodem jest to, że wszystkie łańcuchy w .NET używają unicodu za kulisami. I oczywiście Unicode używa 2 bajty na znak, podczas gdy ASCII używa tylko 1. Więc BinaryFormatter prawdopodobnie nie jest tym, czego można używać w tym przypadku, ponieważ podwajasz ilość danych wysyłanych przez gniazdo.

+0

Tylko uwaga na temat używania binarnego formatera i strumienia pamięci. Trzeba by skonstruować te dwa obiekty za każdym razem, gdy trzeba było konwertować bajty, gdzie tylko za pomocą ASCIIEncodera wywołuje się metodę i to wszystko. Koszt budowy obiektu jest dość wysoki na tak niskim poziomie i może być głównym czynnikiem. – jrista

+0

Doskonały punkt. Może to być coś, co chcesz wziąć pod uwagę tylko w przypadku dużych ciągów, w których długość sznurka równoważy koszt budowy. Oczywiście to wszystko jest teoretyczne (przynajmniej dla mnie). Nie wiem nawet, czy ta metoda byłaby jeszcze szybsza. –

1

Na co starasz się zoptymalizować? PROCESOR? Pasmo?

Jeśli chcesz zoptymalizować przepustowość, możesz wcześniej spróbować skompresować dane ciągu.

Najpierw zrób profil swojego kodu, wymyśl, czym są wolne bity, zanim spróbujesz zoptymalizować na tak niskim poziomie.

+0

+1: Tak, tak, tak –

+0

Optymalizuję dla procesora – Nosrama

+0

Powinieneś także rozważyć * przepustowość magistrali * przepustowości. Podczas wykonywania prostych operacji obliczeniowych na dużych ilościach danych, często procesor spędza większość czasu, czekając na znacznie wolniejszy zegar FSB. – Crashworks

0

Jak powiedzieli inni, klasa Kodowanie jest już zoptymalizowana do tego zadania, więc prawdopodobnie będzie trudniej ją przyspieszyć. Jest jedna mikrooptymalizacja, którą możesz wykonać: użyj zamiast new ASCIIEncoding(). Ale jak każdy wie, mikro optymalizacje są złe;)

1

Bez wskazania wymagań dotyczących współbieżności (lub czegokolwiek innego): Czy można odradzić wątki w wątku, które przekształcają ciągi na tablice bajtów i upuszczają je do kolejki, i mają jeszcze jeden wątek obserwujący kolejkę i wysyłający dane?

0

Proponuję profilowanie tego, co robisz. Uważam, że wątpliwe jest, aby szybkość konwersji łańcucha na tablicę bajtów była większym problemem w wydajności niż prędkość samego gniazda.

+0

W komentarzach, które wyjaśnia, wyprofilował go i prześledził wąskie gardło. – Crashworks

0

Kolejna wskazówka: Nie wiem, jak utworzyć początkowe łańcuchy znaków, ale pamiętaj, że StringBuilder.Append ("coś") jest naprawdę szybszy niż coś takiego jak myString + = "coś".

W całym procesie tworzenia napisów i przesyłania ich przez połączenie z gniazdem, byłbym zaskoczony, gdyby wąskim gardłem było przekształcenie Strings w tablice bajtowe. Ale jestem bardzo zainteresowany, jeśli ktoś przetestuje to z profilerem.

Ben

Powiązane problemy