Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi:快速(er)宽字符串连接_Delphi_Performance_Delphi 5_Widestring - Fatal编程技术网

Delphi:快速(er)宽字符串连接

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

我有一个函数,其工作是将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
         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
    )上,这是一个很好的捕捉
  • 一些HTML结束标记是可选的;逻辑上没有意义的省略

  • 我现在不能花时间给你确切的密码

    但我认为你能做的最快的事情是:

  • 循环遍历所有字符串并计算其总长度,同时添加所需的额外表标记

  • 使用SetString分配一个长度合适的字符串

  • 再次循环遍历所有字符串,并使用将字符串复制到最终字符串中的正确位置


  • 关键是,由于内存的不断分配和释放,许多串接到一个字符串所花费的时间越来越长。一次分配将为您节省最大的时间。

    我没有几万美元可用于支付升级Delphi的所有费用。(还有,我不能忍受IDE或它的性能。我们已经在过渡到一个新的IDE,它是微软的基于C#的免费IDE)而“太多的调整”或“早期优化是万恶之源”的概念在这里并不适用。当时我无法显示屏幕截图,但分析准确地指出了一些甚至最终用户都抱怨的瓶颈。如果不是这么慢的话,我不会在意的。这并不是什么幻想。Stringlist不支持WideString
    SysAllocString
    本身并不比从Delphi的内部堆分配内存慢。通过COM API IMHO可能比从内存管理器池分配进程内存块慢一点。不过,我没有基准测试。@IanBoyd
    SysAllocString
    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;