Delphi:Rápida(er) widestring concatenação
-
26-09-2019 - |
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
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 DomString
s, 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
- Boa captura sobre a força equivocada de todas as cordas na página de código atual (
VarAsString
ao invés deVarAsWideString
) - 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 é:
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á.
Use o SetString para alocar uma string do comprimento adequado.
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.