Pergunta

eu tenho uma função que é a de converter um ADO Recordset em html:

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

E a coragem da função envolve uma série de grande concatenação de seqüência de caracteres:

   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;

Com alguns milhares de resultados, a função tem, o que qualquer usuário poderia sentir, é muito tempo para executar.O Delphi Amostragem Profiler mostra que 99.3% do tempo é gasto em widestring concatenação (@WStrCatN e @WstrCat).

Alguém pode pensar em uma maneira de melhorar widestring concatenação?eu não acho que o Delphi 5 tem qualquer tipo de cadeia de construtor.E Format não oferece suporte a Unicode.


E para se certificar de que ninguém tenta se esquivar:fingir que você está implementando a interface:

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

A Atualização De Um

Eu pensei em usar um IXMLDOMDocument, para construir o HTML como xml.Mas então eu percebi que o HTML final seria xhtml e não html - uma sutil, mas importante, diferença.

Atualização De Dois

Artigo do Microsoft knowledge base: Como Melhorar O Desempenho De Concatenação De Cadeia

Foi útil?

Solução 4

Eu encontrei a melhor solução. O código aberto Htmlparser Para Delphi, tem um ajudante TStringBuilder classe. É usado internamente para construir o que ele chama DomStrings, que é realmente um pseudônimo de WideString:

TDomString = WideString;

Com um pouco de brincadeira de sua classe:

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;

As tripas da rotina se tornam:

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;

O código então sentimentos Para correr infinitamente Afaster. Profiling mostra Muito de melhoria; a WideString A manipulação e a contagem de comprimento tornaram-se insignificantes. Em seu lugar estava as operações internas da Fastmm.

Notas

  1. Boa captura sobre a força equivocada de todas as cordas na página de código atual (VarAsString ao invés de VarAsWideString)
  2. Algumas tags de fechamento HTML são opcionais; omitidos que logicamente não fazem sentido.

Outras dicas

O WideString é inerentemente lento porque foram implementados para compatibilidade com o COM e passam por chamadas COM. Se você olhar para o código, ele continuará realocando a string e ligue para sysallocstringlen () & c que são APIs do OLEAUT32.DLL. Ele não usa o Delphi Memory Manager, mas o AFAIK usa o gerenciador de memória COM. Como a maioria das páginas HTML não usa UTF-16, você pode obter melhor resultado usando o tipo de sequência Delphi nativo e uma lista de strings, embora você deva ter cuidado com a conversão da UTF e a seleção de código real, e a conversão também rebaixará o desempenho . Também você está usando uma função varasstring () que provavelmente converte uma variante para um Ansistring Em seguida, convertido em um wideestring. Verifique se a sua versão do Delphi possui uma função VarasWideString () ou algo parecido para evitá -la ou depender da conversão automática Delphi, se você tiver certeza de que sua variante nunca será nula.

Sim, seu algoritmo está claramente em O (n^2).

Em vez de retornar um string, tente devolver um TStringList, e substitua seu loop por

   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;

Você pode salvar seu Result usando TStringList.SaveToFile

Não consigo gastar o tempo agora para fornecer o código exato.

Mas acho que a coisa mais rápida que você pode fazer é:

  1. Faça um loop através de todas as cordas e totalize seu comprimento também adicionando as tags de tabela extra que você precisará.

  2. Use o SetString para alocar uma string do comprimento adequado.

  3. Percorrer todas as cordas novamente e use o procedimento "mover" Para copiar para a string para o local adequado na sequência final.

O principal é que muitas concatenações para uma string demoram mais e mais por causa da constante alocação e liberação da memória. Uma única alocação será o seu maior time -bar.

Widestring não é contada de referência, qualquer modificação do meio de uma manipulação de seqüência de caracteres. Se o seu conteúdo não é codificados em unicode, você pode usar internamente o nativo de seqüência de caracteres (contada de referência) para concatenar string e, em seguida, convertê-lo para um Widestring.Exemplo é o seguinte:

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

Também tenho visto outra abordagem:Codificar Unicode utf8-string (como referência, contados a), concatená-los e, finalmente, converter utf8-string para Widestring.Mas eu não estou certo de que, se dois utf8-string pode ser concatenado diretamente.O tempo de codificação também deve ser considerado.

De qualquer forma, embora Widestring concatenação é muito mais lento do que o nativo de operações de cadeia de caracteres.Mas é IMO ainda aceitável.Muito tuning sobre esse tipo de coisa deve ser evitada. Considerando seriamente de desempenho, você deve, em seguida, atualizar o seu Delphi para, pelo menos, de 2009.Os custos na compra de uma ferramenta é para longo prazo mais barato do que fazer pesados hacks em um velho Delphi.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top