2009-07-24 20 views
17

En mi binario a texto de la solicitud de decodificación (.NET 2.0) he encontrado que la línea:¿Cómo puedo mejorar el rendimiento del código usando DateTime.ToString?

logEntryTime.ToString("dd.MM.yy HH:mm:ss:fff") 

toma 33% del tiempo de procesamiento total. ¿Alguien tiene alguna idea sobre cómo hacerlo más rápido?

EDITAR: Esta aplicación se utiliza para procesar algunos registros binarios y actualmente demora 15 horas en ejecutarse. Entonces 1/3 de esto será 5 horas.

EDIT: Estoy utilizando NProf para perfilar. La aplicación está procesando alrededor de 17 GBytes de registros binarios.

+4

33% de qué? El 33% del tiempo de procesamiento de 2 ms ni siquiera valdría la pena el refactor. –

+0

Además, ¿qué herramienta usaste para determinar el 33%? Diferentes herramientas a veces miden cosas sutilmente diferentes, por lo que vale la pena saber ... – Stobor

+2

¿Cómo se agrega la cadena resultante a su salida? Stringbuilder? –

Respuesta

13

Desafortunadamente, .NET no tiene un tipo de "formateador" que pueda analizar un patrón y recordarlo.

Si siempre está utilizando el mismo formato, es posible que desee crear un formateador manual para hacer exactamente eso. Algo a lo largo de las líneas de:

public static string FormatDateTime(DateTime dt) 
{ 
    char[] chars = new char[21]; 
    Write2Chars(chars, 0, dt.Day); 
    chars[2] = '.'; 
    Write2Chars(chars, 3, dt.Month); 
    chars[5] = '.'; 
    Write2Chars(chars, 6, dt.Year % 100); 
    chars[8] = ' '; 
    Write2Chars(chars, 9, dt.Hour); 
    chars[11] = ' '; 
    Write2Chars(chars, 12, dt.Minute); 
    chars[14] = ' '; 
    Write2Chars(chars, 15, dt.Second); 
    chars[17] = ' '; 
    Write2Chars(chars, 18, dt.Millisecond/10); 
    chars[20] = Digit(dt.Millisecond % 10); 

    return new string(chars); 
} 

private static void Write2Chars(char[] chars, int offset, int value) 
{ 
    chars[offset] = Digit(value/10); 
    chars[offset+1] = Digit(value % 10); 
} 

private static char Digit(int value) 
{ 
    return (char) (value + '0'); 
} 

Esto es bastante feo, pero es probable que sea mucho más eficiente ... de referencia que, por supuesto!

+0

lol; ¡Creo que estábamos trabajando en un código muy similar! –

+1

@Marc ¡las mentes geniales piensan igual! – inspite

+8

... y los tontos rara vez difieren :) –

9

¿Estás seguro que toma el 33% del tiempo? ¿Cómo has medido eso? Suena más que un poco sospechoso para mí ...

Esto hace las cosas un poco más rápidos poco :

Basic: 2342ms 
Custom: 1319ms 

O si cortamos la IO (Stream.Null):

Basic: 2275ms 
Custom: 839ms 

using System.Diagnostics; 
using System; 
using System.IO; 
static class Program 
{ 
    static void Main() 
    { 
     DateTime when = DateTime.Now; 
     const int LOOP = 1000000; 

     Stopwatch basic = Stopwatch.StartNew(); 
     using (TextWriter tw = new StreamWriter("basic.txt")) 
     { 
      for (int i = 0; i < LOOP; i++) 
      { 
       tw.Write(when.ToString("dd.MM.yy HH:mm:ss:fff")); 
      } 
     } 
     basic.Stop(); 
     Console.WriteLine("Basic: " + basic.ElapsedMilliseconds + "ms"); 

     char[] buffer = new char[100]; 
     Stopwatch custom = Stopwatch.StartNew(); 
     using (TextWriter tw = new StreamWriter("custom.txt")) 
     { 
      for (int i = 0; i < LOOP; i++) 
      { 
       WriteDateTime(tw, when, buffer); 
      } 
     } 
     custom.Stop(); 
     Console.WriteLine("Custom: " + custom.ElapsedMilliseconds + "ms"); 
    } 
    static void WriteDateTime(TextWriter output, DateTime when, char[] buffer) 
    { 
     buffer[2] = buffer[5] = '.'; 
     buffer[8] = ' '; 
     buffer[11] = buffer[14] = buffer[17] = ':'; 
     Write2(buffer, when.Day, 0); 
     Write2(buffer, when.Month, 3); 
     Write2(buffer, when.Year % 100, 6); 
     Write2(buffer, when.Hour, 9); 
     Write2(buffer, when.Minute, 12); 
     Write2(buffer, when.Second, 15); 
     Write3(buffer, when.Millisecond, 18); 
     output.Write(buffer, 0, 21); 
    } 
    static void Write2(char[] buffer, int value, int offset) 
    { 
     buffer[offset++] = (char)('0' + (value/10)); 
     buffer[offset] = (char)('0' + (value % 10)); 
    } 
    static void Write3(char[] buffer, int value, int offset) 
    { 
     buffer[offset++] = (char)('0' + (value/100)); 
     buffer[offset++] = (char)('0' + ((value/10) % 10)); 
     buffer[offset] = (char)('0' + (value % 10)); 
    } 
} 
+2

No lo creo del todo: cuando hice algunas pruebas de registro, encontré que el formato y el análisis de fechas y horas dominaban el acceso a la CPU. Es cierto que no estaba haciendo prácticamente nada más que iniciar sesión ... –

+0

Su microentorno está vinculado a IO, si desea medir los ciclos de la CPU, probablemente debería deshacerse de la escritura en un archivo. –

+1

Sí, pero el OP está escribiendo en un archivo, por lo que es un poco artificial ignorar esto ... pero de hecho, usar Stream.Null mostraría más la diferencia. –

0

¿Sabes cuán grande es cada registro en el binario y el texto? los registros van a ser? De ser así, puede dividir el procesamiento del archivo de registro entre varios hilos que darían un mejor uso a una PC con núcleo/procesador múltiple. Si no le molesta que el resultado se encuentre en archivos separados, sería una buena idea tener un disco duro por núcleo, de esa forma reducirá la cantidad que las cabezas de disco deben moverse.

1

Esta no es una respuesta en sí mismo, sino más bien una addedum a la respuesta de unos excelentes de Jon Skeet, ofreciendo una variante de la "s" formato (ISO):

/// <summary> 
    ///  Implements a fast method to write a DateTime value to string, in the ISO "s" format. 
    /// </summary> 
    /// <param name="dateTime">The date time.</param> 
    /// <returns></returns> 
    /// <devdoc> 
    ///  This implementation exists just for performance reasons, it is semantically identical to 
    ///  <code> 
    /// text = value.HasValue ? value.Value.ToString("s") : string.Empty; 
    /// </code> 
    ///  However, it runs about 3 times as fast. (Measured using the VS2015 performace profiler) 
    /// </devdoc> 
    public static string ToIsoStringFast(DateTime? dateTime) { 
     if (!dateTime.HasValue) { 
      return string.Empty; 
     } 
     DateTime dt = dateTime.Value; 
     char[] chars = new char[19]; 
     Write4Chars(chars, 0, dt.Year); 
     chars[4] = '-'; 
     Write2Chars(chars, 5, dt.Month); 
     chars[7] = '-'; 
     Write2Chars(chars, 8, dt.Day); 
     chars[10] = 'T'; 
     Write2Chars(chars, 11, dt.Hour); 
     chars[13] = ':'; 
     Write2Chars(chars, 14, dt.Minute); 
     chars[16] = ':'; 
     Write2Chars(chars, 17, dt.Second); 
     return new string(chars); 
    } 

Con el serializador 4 dígitos como :

private static void Write4Chars(char[] chars, int offset, int value) { 
     chars[offset] = Digit(value/1000); 
     chars[offset + 1] = Digit(value/100 % 10); 
     chars[offset + 2] = Digit(value/10 % 10); 
     chars[offset + 3] = Digit(value % 10); 
    } 

Esto cuesta alrededor de 3 veces más rápido. (Medido utilizando el perfilador de rendimiento VS2015)

Cuestiones relacionadas