TMemoryStream的性能问题

TMemoryStream作为使用非常多的一个Stream类,日常使用时并没有感觉到慢,这是因为代码中使用频度不高的缘故,当使用频度一上去,TMemoryStream的性能简直不忍直视。好了,我们看看原始代码是怎么写的:


function TMemoryStream.Realloc(var NewCapacity: Longint): Pointer;
begin
  if (NewCapacity > 0) and (NewCapacity <> FSize) then
    NewCapacity := (NewCapacity + (MemoryDelta - 1)) and not (MemoryDelta - 1);
  Result := Memory;
  if NewCapacity <> FCapacity then
  begin
    if NewCapacity = 0 then
    begin
{$IFDEF MSWINDOWS}
      GlobalFreePtr(Memory);
{$ELSE}
      FreeMem(Memory);
{$ENDIF}
      Result := nil;
    end else
    begin
{$IFDEF MSWINDOWS}
      if Capacity = 0 then
        Result := GlobalAllocPtr(HeapAllocFlags, NewCapacity)
      else
        Result := GlobalReallocPtr(Memory, NewCapacity, HeapAllocFlags);
{$ELSE}
      if Capacity = 0 then
        GetMem(Result, NewCapacity)
      else
        ReallocMem(Result, NewCapacity);
{$ENDIF}
      if Result = nil then raise EStreamError.CreateRes(@SMemoryStreamError);
    end;
  end;
end;

我们知道,TMemoryStream最终操纵的是内存,那么分配内存自然是对性能影响最大的部分,反映在代码上就是下面红色的两行代码:

if (NewCapacity > 0) and (NewCapacity <> FSize) then
    NewCapacity := (NewCapacity + (MemoryDelta - 1)) and not (MemoryDelta - 1);

根据这两行代码,我们可以看出,每次最少分配8K内存,然后以8K的倍数进行增长。所以当向TMemoryStream写入N*8K数据时,理论上要分配N次内存,对于小于8K或者使用次数很少的MemoryStream来说完全不是问题。但是,一旦写入数据很多,就非常非常麻烦了。

知道原因后,解决方案就很简单了:

1。在写入前直接分配足够的内存来避免多次分配,使用TMemoryStream.Size = 你需要的内存数量(例如1024 *1024);来预先分配足够的内存来使用。

2。使用自己的内存分配策略来分配内存。其实也很简单,从TMemoryStream继承下来然后动态更改NewCapacity的值即可。例如:当所需内存少于8K时还是直接分配8K,但大于8K且小于64M时一次增长原有内存的2倍(或者4倍或者8倍),大于64M且小于1G时一次增长0.5倍,超过1G时每次增长128M。这个内存分配策略只是举例,可以根据自己的需要来进行调整。伪代码大概像这样:


  if (NewCapacity > 0) and (NewCapacity <> FSize) then
  begin
    if FSize = 0 then
      iCapacity := NewCapacity shl 1
    else if FSize < C_64M then
      iCapacity := FSize shl 1
    else if FSize < C_1G then
      iCapacity := FSize + FSize shr 1
    else
      iCapacity := FSize + C_128M;

    if iCapacity < NewCapacity then
      iCapacity := NewCapacity shl 1;

    if iCapacity < FMemoryDelta then
      iCapacity := FMemoryDelta;
    if iCapacity > MaxInt then
      iCapacity := MaxInt;

    NewCapacity := iCapacity;

    NewCapacity := (NewCapacity + (FMemoryDelta - 1)) and not (FMemoryDelta - 1);
  end;


这样更改后,MemoryStream的性能可以有1~3个数量级的提升,这时才能真正展现MemoryStream的威力。嗯,内存的快不是你想象中的快,而是超级快!

其实,MemoryStream的原始代码毕竟写于20年前,那时的PC机内存大概在8~16M左右,因此原有的内存分配代码在当时是无可挑剔的,现在的情况完全不一样了,毕竟动不动16G、64G的内存标配让原有的内存分配代码成为了瓶颈,这也是历史原因和历史遗留问题。还好,我们可以快速修正。

最后,顺便说说TList的问题,同样,TList也是20年前的代码,在进行元素添加或者删除时同样存在内存分配和内存移动的问题。相信在简单的更改后你也能轻松把性能提升1~3个数量级!Good Luck!



  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值