Pregunta

Tengo una función que es trabajo consiste en convertir un ADO Recordset en html:

class function RecordsetToHtml(const rs: _Recordset): WideString;

Y las entrañas de la función implica una gran cantidad de ancho concatenación de cadenas:

   while not rs.EOF do
   begin
      Result := Result+CRLF+
         '<TR>';

      for i := 0 to rs.Fields.Count-1 do
         Result := Result+'<TD>'+VarAsWideString(rs.Fields[i].Value)+'</TD>';

      Result := Result+'</TR>';
      rs.MoveNext;
    end;

Con unos pocos miles de resultados, la función toma, lo que se sentiría cualquier usuario, es demasiado largo para funcionar. El Delphi muestreo Profiler muestra que 99,3% del tiempo se gasta en widestring concatenación (@WStrCatN y @WstrCat).

Puede alguien pensar en una manera de mejorar la concatenación widestring? No creo Delphi 5 tiene ningún tipo de generador de cadenas. Y Format no admite Unicode.


Y para asegurarse de que nadie trate de escabullirse: pretender que esté implementando la interfaz:

IRecordsetToHtml = interface(IUnknown)
    function RecordsetToHtml(const rs: _Recordset): WideString;
end;

Actualizar Uno

I pensó en utilizar un IXMLDOMDocument, para construir el HTML como XML. Pero luego me di cuenta de que el HTML final sería xhtml y no html - una sutil pero importante diferencia

.

Actualizar Dos

artículo de la base de conocimientos de Microsoft: Cómo mejorar Concatenación de cadenas Rendimiento

¿Fue útil?

Solución 4

He encontrado la mejor solución. El código abierto HtmlParser para Delphi, tiene una clase de ayuda TStringBuilder. Se utiliza internamente para construir lo que llama DomStrings, que es en realidad un alias de WideString:

TDomString = WideString;

Con un poco de tocar el violín de su clase:

TStringBuilder = class
public
   constructor Create(ACapacity: Integer);
   function EndWithWhiteSpace: Boolean;
   function TailMatch(const Tail: WideString): Boolean;
   function ToString: WideString;
   procedure AppendText(const TextStr: WideString);
   procedure Append(const value: WideString);
   procedure AppendLine(const value: WideString);
   property Length: Integer read FLength;
end;

Las tripas de la rutina se convierte en:

while not rs.EOF do
begin
   sb.Append('<TR>');

   for i := 0 to rs.Fields.Count-1 do
      sb.Append('<TD>'+VarAsWideString(rs.Fields[i].Value));

   sb.AppendLine('</TR>');

   rs.MoveNext;
end;

A continuación el código siente para ejecutar infinitamente afaster. espectáculos de perfiles mucho mejora; la manipulación WideString y duración de conteo se hicieron insignificante. En su lugar había propias operaciones internas de FastMM.

Notas

  1. Buena atrapada en la errónea forzando de todas las cadenas en corriente de página de códigos (VarAsString en lugar de VarAsWideString)
  2. Algunas etiquetas HTML de cierre son opcionales; omitidos los que lógicamente no tienen sentido.

Otros consejos

WideString son inherentemente lento porque se implementaron para COM compatibilidad y pasan a través de llamadas COM. Si nos fijamos en el código, que se mantendrá la reasignación de la cadena y llame SysAllocStringLen () y C que son las API de oleaut32.dll. No utiliza el administrador de memoria Delphi, pero que yo sepa que utiliza el administrador de memoria COM. Debido a que la mayoría de las páginas HTML no usan UTF-16, es posible obtener mejores resultados utilizando el tipo nativo cadena de Delphi y una lista de cadenas, aunque se debe tener cuidado con la conversión de UTF y la página de códigos actual, y la conversión degradar el rendimiento, así . También está utilizando una función VarAsString () que convierte probablemente una variante de un AnsiString luego se convierte en un WideString. Compruebe si su versión de Delphi tiene un VarAsWideString () o la función por igual algo para evitarlo, o se basan en la conversión automática de Delphi si pudiera estar seguro de que su variante nunca será NULL.

Sí, su algoritmo es claramente en O (n ^ 2).

En lugar de devolver un string, trata de devolver un TStringList, y reemplazar su lazo con

   while not rs.EOF do
   begin
      Result.Add('<TR>');

      for i := 0 to rs.Fields.Count-1 do
         Result.Add( '<TD>'+VarAsString(rs.Fields[i].Value)+'</TD>' );

      Result := Result.Add('</TR>');
      rs.MoveNext;
    end;

A continuación, puede guardar su Result usando TStringList.SaveToFile

Soy incapaz de pasar el tiempo en este momento para darle el código exacto.

Sin embargo, creo que la cosa más rápida que puede hacer es:

  1. bucle a través de todas las cuerdas y el total de su longitud, además de darle a las etiquetas de tabla adicionales que necesita.

  2. Uso SetString para asignar una cadena de la longitud adecuada.

  3. Bucle a través de todas las cadenas de nuevo y utilizar el "movimiento" procedimiento para copiar a la cadena en el lugar apropiado en la cadena final.

La clave es que muchas concatenaciones a una toma de cadena más largos debido a la constante de asignar y liberar memoria. Una única asignación será su mayor ahorro de tiempo.

WideString no se referencia contado, cualquier modificación significa una manipulación de cadenas. Si el contenido no está codificado con Unicode , puede utilizar internamente la cadena nativa (referencia contado) para concatenar cadena y luego convertirlo a un WideString. Ejemplo es como sigue:

var
  NativeString: string;
begin
   // ...
   NativeString := '';

   while not rs.EOF do
   begin
     NativeString := NativeString + CRLF + '<TR>';

     for i := 0 to rs.Fields.Count-1 do
       NativeString := NativeString + '<TD>'+VarAsString(rs.Fields[i].Value) + '</TD>';

     NativeString := NativeString + '</TR>';
     rs.MoveNext;
   end;

   Result := WideString(NativeString);

I también han visto otro enfoque: Codificar Unicode para UTF8String (como referencia contado), ellos concatenar y finalmente convertir UTF8String a WideString. Pero no estoy seguro, si dos UTF8String se puede concatenar directamente. El tiempo de codificación también debe ser considerado.

De todos modos, aunque WideString concatenación es mucho más lento que las operaciones de cadenas nativas. Pero es todavía aceptable OMI. El exceso de sintonía en este tipo de cosas debe ser evitado. En serio teniendo en cuenta el rendimiento, a continuación, debe actualizar su Delphi a por lo menos 2009. Los gastos para la compra de una herramienta es para largo plazo más barato que hacer cortes más fuertes a una antigua Delphi.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top