Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/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中的字符串连接速度比Java快? 问题_Java_String_Delphi_Concatenation - Fatal编程技术网

为什么Delphi中的字符串连接速度比Java快? 问题

为什么Delphi中的字符串连接速度比Java快? 问题,java,string,delphi,concatenation,Java,String,Delphi,Concatenation,我写了两个程序,一个在Delphi中,一个在Java中,用于字符串连接,我注意到Delphi中的字符串连接比Java快得多 JAVA 问题: 两者都以毫秒为单位测量时间,但Delphi程序的速度要快得多,分别为1ms和2秒。为什么在Delphi中字符串连接要快得多 编辑:带着更多的经验回顾这个问题,我应该得出这样的结论:主要的区别在于编译Delphi和编译Java,然后在JVM中运行。在Java(和C#)中,字符串是不可变的对象。这意味着,如果您有: string s=“string 1” 然后

我写了两个程序,一个在Delphi中,一个在Java中,用于字符串连接,我注意到Delphi中的字符串连接比Java快得多

JAVA 问题: 两者都以毫秒为单位测量时间,但Delphi程序的速度要快得多,分别为1ms和2秒。为什么在Delphi中字符串连接要快得多

编辑:带着更多的经验回顾这个问题,我应该得出这样的结论:主要的区别在于编译Delphi和编译Java,然后在JVM中运行。

在Java(和C#)中,字符串是不可变的对象。这意味着,如果您有:

string s=“string 1”

然后编译器为这个字符串分配内存。那么避风港

s = s + " String 2"
按预期为我们提供“字符串1字符串2”,但由于字符串的不变性,分配了一个新字符串,其大小正好包含“字符串1字符串2”,并且将两个字符串的内容复制到新位置。然后垃圾收集器删除原始字符串。在Delphi中,字符串更容易“写时复制”和引用计数,这要快得多

C#和Java类的行为非常类似于Delphi字符串,在修改和操作字符串时速度非常快。

TLDR 可能还有其他因素,但Delphi的默认内存管理器肯定是一个很大的贡献者。它的设计是为了减少内存重新分配的频率,从而有点浪费空间

考虑内存管理器开销 当您有一个直接的内存管理器(您甚至可以称之为“naive”)时,您的循环连接字符串实际上更像:

//pseudo-code
for I := 1 to 50000 do
begin
  if CanReallocInPlace(Str) then
    //Great when True; but this might not always be possible.
    ReallocMem(Str, Length(Str) + Length(Txt))
  else
  begin
    AllocMem(NewStr, Length(Str) + Length(Txt))
    Copy(Str, NewStr, Length(Str))
    FreeMem(Str)
  end;
  Copy(Txt, NewStr[Length(NewStr)-Length(Txt)], Length(Txt))
end;
请注意,每次迭代都会增加分配。如果你运气不好,你常常不得不:

  • 在新位置分配内存
  • 复制现有的“字符串到目前为止”
  • 最后释放旧字符串
Delphi(和FastMM) 但是,Delphi已从早期使用的默认内存管理器切换到以前的第三方(FastMM),其设计运行速度更快,主要原因是:

  • (1) 使用子分配器,即从操作系统一次获取一个“大”页面的内存
  • 然后从页面执行分配,直到页面用完
  • 然后才从操作系统获得另一个页面
  • (2) 积极地分配比请求的内存更多的内存(预计会有少量增长)
  • 然后,稍微大一点的请求就更有可能被重新分配到位
  • 这些技术可以认为不能保证提高性能
  • 但它确实浪费了空间。(不幸的是,碎片化造成的浪费可能相当严重。)
结论 当然,您为演示性能而编写的简单应用程序从新的内存管理器中获益匪浅。您运行一个循环,在每次迭代中递增地重新分配字符串。希望有尽可能多的到位分配

您可以尝试通过在循环中强制额外分配来规避FastMM的一些性能改进。(尽管页面的子分配仍然有效。)
因此,最简单的方法是尝试使用较旧的Delphi编译器(如D5)来演示这一点

FWIW:字符串生成器
你说你“不想使用字符串生成器”。但是,我想指出,字符串生成器也有类似的好处。具体来说(如果按预期实现):字符串生成器不需要一直重新分配子字符串。当最终构建字符串时;正确的内存量可以在一个步骤中分配,“构建字符串”的所有部分都复制到它们所属的位置。

因为它可能是一种不同的语言?我会说:因为delphi是用本机代码编译的,java不是(但仍然有JIT,所以我可能错了)。但也许你应该给我们看两种代码,“更快”有多快?你说的是快1%还是5%,还是快10倍?这取决于你的代码是什么样子的?也许您的Java代码没有以最佳方式工作?或者可能是因为Delphi是100%本机的(如前所述)?可能在java中,当连接新字符串时,您使用了字符串而不是StringBuilder。所以这个答案是不好的。用于concatention的RTL内部(至少在XE4中是USRCAT)使用目标扩展。也就是说,如果可能(只允许对字符串和内存进行一次引用),可以使用realloc,而不是alloc+副本。显然,在某些情况下,alloc+拷贝(可能是免费的)仍然是必要的。串联可以创建新字符串,但将尝试不创建新字符串。这是写时拷贝模型的逻辑行为;我希望自从引入ref.counted strings.PS以来,这一直是Delphi字符串处理的一部分。强烈建议将其用于在循环中连接字符串的代码,即使对于Delphi@MaxAbramovich这取决于所使用的堆管理器的类型。有文章将不同的字符串构建模式与不同的MMs进行了比较。TStringBuilder的性能往往不太令人印象深刻。哇,谢谢你的回答!我知道StringBuilder提供了一些好处,我可能会使用它,但我想知道为什么在delphi中“string+string”要快得多。我只是需要这些信息在universaty上做一个关于字符串生成器的演示——好吧,是的,理论上就是这样。TList在O(SQRT(N))伸缩方面会更加有效。但现实情况是,字符串操作无处不在,在XE2之前或XE6之后的Delphi中,编译器生成了相当紧凑的代码,再加上FastMM针对该字符串操作用例的调优。我猜你读过那些文章,他们比较了构建字符串的不同方法和不同的堆管理器。使用FastMM时,TStringBuilder非常重要
s = s + " String 2"
//pseudo-code
for I := 1 to 50000 do
begin
  if CanReallocInPlace(Str) then
    //Great when True; but this might not always be possible.
    ReallocMem(Str, Length(Str) + Length(Txt))
  else
  begin
    AllocMem(NewStr, Length(Str) + Length(Txt))
    Copy(Str, NewStr, Length(Str))
    FreeMem(Str)
  end;
  Copy(Txt, NewStr[Length(NewStr)-Length(Txt)], Length(Txt))
end;