【问题标题】:How do I speedup the copy of a generic array?如何加快通用数组的复制速度?
【发布时间】:2014-09-01 20:04:34
【问题描述】:

我有一个简单的数组:

Myarray<T> = array[0..100] of T;

如何尽快将其复制到另一个数组中。 请注意,源始终是普通的通用数组,而目标可以是动态数组。 (但我怀疑这很重要)。

我试过了

move(Source[0], dest[0], count * sizeof(T));

但这对于托管类型并不好。

我希望得到比以下更快的东西:

for i:= 0 to count -1 do D[i]:= S[i];

我有什么选择?

背景
该数组是 BTree 结构的一部分,并且正在进行大量复制。
我需要速度。

【问题讨论】:

  • 您为什么希望可以改进简单的作业?简单赋值有什么问题?
  • 没有移动那么快。我想要介于两者之间的东西。
  • 但是这些动作不起作用。作业可以。它在什么方面效率低下?我猜这个问题会因为所有的代码而混淆。您只需要一种性能更好的分配方式。我认为你应该问这个一般性问题,而不是展示我们不需要看到的所有其他代码。 Q 的根源是分配的性能。请问你能不能问一下。你的许多问题都是这样的,你提出了很多无关紧要的细节,隐藏了问题的核心。
  • 它没有问题,但我希望它更快。
  • 这些动作只适用于简单类型,也许我应该对这种情况进行特殊处理并循环处理其他所有内容。

标签: arrays delphi generics copy


【解决方案1】:

我在 Google+ 上问过同样的问题 - https://plus.google.com/u/0/116430453567926016001/posts/b1u1shzkmrW

代码由 Stefan Glienke 提供,我在这里重新发布了代码,对两种变量类型进行了细微更改,因此它是 64 位兼容的。

uses
  TypInfo,
  SysUtils;

type
  TArray = record
    class function Clone<T>(const source: T): T; static;
  end;

function DynArrayLength(const A: Pointer): NativeInt;
type
  PDynArrayRec = ^TDynArrayRec;
  TDynArrayRec = packed record
  {$IFDEF CPUX64}
    _Padding: LongInt;
  {$ENDIF}
    RefCnt: LongInt;
    Length: NativeInt;
  end;
begin
  Result := 0;
  if A <> nil then
    Result := PDynArrayRec(PByte(A) - SizeOf(TDynArrayRec))^.Length;
end;

{$POINTERMATH ON}
type
  PArray = ^Pointer;
{$POINTERMATH OFF}

procedure CopyArrayDeep(Dest, Source: PArray; TypeInfo: Pointer; Count: NativeInt);
var
  typeData: PTypeData;
  elType: PPTypeInfo;
  i: Integer;
len : NativeInt;
begin
  if (Source = nil) or (Count = 0) then
    Exit;
  typeData := GetTypeData(TypeInfo);
  elType := typeData.elType;
  if (elType <> nil) and (elType^.kind = tkDynArray) then
  begin
    for i := 0 to Count-1 do
    begin
      len := DynArrayLength(Source[i]);
      DynArraySetLength(Dest[i], elType^, 1,@len);
      CopyArrayDeep(Dest[i], Source[i], elType^, len);
    end;
  end
  else
    if elType <> nil then
      CopyArray(Dest, Source, elType^, Count)
    else
      Move(Source^, Dest^, Count * typeData.elSize);
end;

class function TArray.Clone<T>(const source: T): T;
var
  p: PTypeInfo;
  count: NativeInt;
  i: Integer;
begin
  p := TypeInfo(T);
  if p.Kind <> tkDynArray then
    raise ENotSupportedException.Create('type not supported');
  count := DynArrayLength(PArray(@source)^);
  DynArraySetLength(PArray(@Result)^, p, 1,@count);
  CopyArrayDeep(PArray(@Result)^, PArray(@source)^, p, count);
end;

【讨论】:

  • 请注意@StefanGlienke 制作的 cmets,可以将其扩展为包括嵌套托管类型(字符串、动态数组等)。
  • 我不明白这与问题有什么关系
  • @DavidHeffernan 问题是“如何加快泛型数组的副本”,我的回答涵盖“泛型数组的副本”部分。它涵盖了发布者的问题“但这与托管类型不兼容”。和“我有什么选择?”唯一没有涵盖的部分是加速问题,但由于没有时间,我不知道是否更快 - 我怀疑它是因为它使用 RTL 但这只是我的猜测。所以对我来说,它与问题有很大关系,但没有完全回答问题,但没有时间,怎么能做到?
  • 这里的代码看起来和for i:= 0 to count -1 do D[i]:= S[i];的语义不太一样
  • @DavidHeffernan 除了for i:= 0 to count -1 do D[i]:= S[i];是浅拷贝,而给出的例程是深拷贝。
猜你喜欢
  • 2014-01-14
  • 2019-04-14
  • 2019-07-18
  • 2016-09-21
  • 1970-01-01
  • 1970-01-01
  • 2019-10-01
  • 2015-01-18
  • 1970-01-01
相关资源
最近更新 更多