Delphi:快速(er)宽字符串连接
我有一个函数,其工作是将ADO转换为html:Delphi:快速(er)宽字符串连接,delphi,performance,delphi-5,widestring,Delphi,Performance,Delphi 5,Widestring,我有一个函数,其工作是将ADO转换为html: class function RecordsetToHtml(const rs: _Recordset): WideString; 函数的核心部分包括大量宽字符串连接: while not rs.EOF do begin Result := Result+CRLF+ '<TR>'; for i := 0 to rs.Fields.Count-1 do Resul
class function RecordsetToHtml(const rs: _Recordset): WideString;
函数的核心部分包括大量宽字符串连接:
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;
更新一 我考虑使用
IXMLDOMDocument
,将HTML构建为xml。但后来我意识到最终的HTML将是xhtml
,而不是HTML
——一个微妙但重要的区别
更新二
Microsoft知识库文章:是的,您的算法显然是O(n^2) 不要返回
字符串
,而是尝试返回TStringList
,并将循环替换为
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;
而不是rs.EOF do
开始
结果。添加(“”);
对于i:=0到rs.Fields.Count-1 do
结果.Add(''+VarAsString(rs.Fields[i].Value)+'');
结果:=结果。添加(“”);
下一步;
结束;
然后,您可以使用
TStringList保存结果
。SaveToFile
宽字符串不包含引用计数,任何修改都意味着字符串操作如果您的内容不是unicode编码的,您可以在内部使用本机字符串(引用计数)连接字符串,然后将其转换为宽字符串。示例如下:
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);
var
国家投资:字符串;
开始
// ...
本地投资:='';
而不是rs.EOF
开始
NativeString:=NativeString+CRLF+'';
对于i:=0到rs.Fields.Count-1 do
NativeString:=NativeString+''+VarAsString(rs.Fields[i].Value)+'';
NativeString:=NativeString+'';
下一步;
结束;
结果:=宽字符串(NativeString);
我还看到了另一种方法:将Unicode编码为UTF8String(按引用计数),连接它们,最后将UTF8String转换为Widestring。但我不确定两个UTF8String是否可以直接连接。还应考虑编码时间
无论如何,尽管宽字符串连接比本机字符串操作慢得多。但这仍然是可以接受的。应该避免对这类事情进行过多调整。认真考虑性能,您应该将您的Delphi升级到至少2009年。购买一个工具的成本要比在一个旧的Delphi上进行大量的黑客攻击便宜得多。WideString本质上是缓慢的,因为它们是为了COM兼容性和通过COM调用实现的。如果查看代码,它将继续重新分配字符串并调用SysAllocStringLen()&C,这是来自oleaut32.dll的API。它不使用Delphi内存管理器,但它使用COM内存管理器。 因为大多数HTML页面不使用UTF-16,所以使用本机Delphi字符串类型和字符串列表可能会获得更好的结果,尽管您应该注意从UTF和实际代码页的转换,并且转换也会降低性能。
此外,您还使用了VarAsString()函数,该函数可能会将变量转换为AnsiString,然后再转换为WideString。检查您的Delphi版本是否有VarAsWideString()或类似的函数来避免它,或者如果您可以确保您的变量永远不会为空,请使用Delphi自动转换。我找到了最佳解决方案。Delphi的开放源码,有一个helper
TStringBuilder
类。它在内部用于构建他所称的DomString
s,实际上是WideString
的别名:
TDomString = WideString;
在他的课堂上做了一点手脚:
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;
例行程序的核心是:
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;
而不是rs.EOF do
开始
某人附加(“”);
对于i:=0到rs.Fields.Count-1 do
sb.Append(“”+VarAsWideString(rs.Fields[i].Value));
某人附言(“”);
下一步;
结束;
然后,代码感觉运行得非常快。分析显示有很大的改善;WideString
操作和长度计数变得微不足道。取而代之的是FastMM自己的内部运营
注释
VarAsString
而不是VarAsWideString
)上,这是一个很好的捕捉我现在不能花时间给你确切的密码 但我认为你能做的最快的事情是:
关键是,由于内存的不断分配和释放,许多串接到一个字符串所花费的时间越来越长。一次分配将为您节省最大的时间。我没有几万美元可用于支付升级Delphi的所有费用。(还有,我不能忍受IDE或它的性能。我们已经在过渡到一个新的IDE,它是微软的基于C#的免费IDE)而“太多的调整”或“早期优化是万恶之源”的概念在这里并不适用。当时我无法显示屏幕截图,但分析准确地指出了一些甚至最终用户都抱怨的瓶颈。如果不是这么慢的话,我不会在意的。这并不是什么幻想。Stringlist不支持WideString
SysAllocString
本身并不比从Delphi的内部堆分配内存慢。通过COM API IMHO可能比从内存管理器池分配进程内存块慢一点。不过,我没有基准测试。@IanBoydSysAllocString
和SysFreeString
比Delphi的FastMM4慢得多。只要替换API调用,在分配大量的WideString
实例时,您就可以获得50倍的加速—请参阅理想的解决方案,一个
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;