在从磁盘读取数据(数据仅指字符串)的函数中,我应该更喜欢哪种?哪个更好?
A) DiskStream.Read(Pointer(s)^, Count)
or
B) DiskStream.Read(s[1], Count)
Run Code Online (Sandbox Code Playgroud)
注意:
我知道两者都有相同的结果.
我知道在调用Read之前我必须使用S的SetLength.
UPDATE
S是AnsiString.
这是完整的功能:
{从文件中读取一堆字符.为什么'ReadChars'而不是'ReadString'?此函数读取C++字符串(字符串的长度也未写入磁盘).所以,我必须给出作为参数读取的字符数.}
function TMyStream.ReadChars(out s: AnsiString; CONST Count: Longint): Boolean;
begin
SetLength(s, Count);
Result:= Read(s[1], Count)= Count;
end;
Run Code Online (Sandbox Code Playgroud)
速度测试
在我的速度测试中,第一种方法比第二种方法快一点.我使用了一个400MB的文件,我从中读取了大约200000次字符串.该过程设置为高优先级.
有史以来最好的阅读时间是:
变体B为1.35,变体A为1.37.
平均:
平均而言,B得分也比A好20ms.
对于每种变体,测试重复15次.
差别很小.它可能落入测量误差范围.如果我更频繁地从更大的文件中读取字符串,那么可能会很重要.但就目前而言,我们可以说两行代码都表现相同.
ANSWER
Variant A - 可能是一点点微小的变种B - (显然)更容易阅读,它更像是Delphi-ish.我的首选.
注意:
我在TStreamReadBuffer示例中看到了Embarcadero使用变量A,但是使用TBytes而不是String.
Mas*_*ler 18
绝对是数组符号.Delphi风格的一部分是使您的代码易于阅读,并且当您详细说明您正在做的事情时,更容易分辨出正在发生的事情.将字符串转换为指针然后取消引用它看起来很混乱; 你为什么做那?除非读者对字符串内部有很多了解,否则它没有意义.
Arn*_*hez 13
运行时请注意
1. DiskStream.Read(Pointer(s)^, Count)
2. DiskStream.Read(s[1], Count)
Run Code Online (Sandbox Code Playgroud)
1.版本会更快.
但是你必须确保s变量是显式本地的,或者你UniqueString(s)在循环之前调用了自己.
由于pointer(s)^不会调用UniqueString?()低级隐藏RTL调用,它会比它更快s[1],但是如果s字符串变量在当前上下文和其他上下文之间共享,则可以覆盖一些现有数据(例如,如果s从函数中检索到最后一个内容)从属性值,或s作为参数发送到另一个方法).
实际上,AnsiString从内容中编码此读取的最快正确方法是:
s := '';
SetLength(s,Count);
DiskStream.Read(pointer(s)^,Count);
Run Code Online (Sandbox Code Playgroud)
要么
SetString(s,nil,Count);
DiskStream.Read(pointer(s)^,Count);
Run Code Online (Sandbox Code Playgroud)
第二个版本等于第一个版本,但少了一行.
设置s为''将调用FreeMem()+AllocMem()而不是ReallocMem()in SetLength(),因此将避免调用,因此会move()更快一些.
实际上,UniqueString?()生成的RTL调用s[1]将非常快,因为您SetLength()在调用它之前已经调用过:因此,s它已经是唯一的,并且UniqueString?()RTL调用几乎会立即返回.在分析之后,两个版本之间没有太大的速度差异:几乎所有的时间花在字符串分配和内容从磁盘移动.也许s[1]被发现更"邋"".
如果您关心优化,您应该更喜欢第一个变体.只需看看编译器生成的代码:
Unit7.pas.98: Stream.Read(Pointer(S)^, 10);
00470EA9 8B55FC mov edx,[ebp-$04]
00470EAC B90A000000 mov ecx,$0000000a
00470EB1 8BC6 mov eax,esi
00470EB3 8B18 mov ebx,[eax]
00470EB5 FF530C call dword ptr [ebx+$0c]
Unit7.pas.99: Stream.Read(s[1], 10);
00470EB8 8B5DFC mov ebx,[ebp-$04]
00470EBB 85DB test ebx,ebx
00470EBD 7418 jz $00470ed7
00470EBF 8BC3 mov eax,ebx
00470EC1 83E80A sub eax,$0a
00470EC4 66833802 cmp word ptr [eax],$02
00470EC8 740D jz $00470ed7
00470ECA 8D45FC lea eax,[ebp-$04]
00470ECD 8B55FC mov edx,[ebp-$04]
00470ED0 E8CB3FF9FF call @InternalUStrFromLStr
00470ED5 8BD8 mov ebx,eax
00470ED7 8D45FC lea eax,[ebp-$04]
00470EDA E89950F9FF call @UniqueStringU
00470EDF 8BD0 mov edx,eax
00470EE1 B90A000000 mov ecx,$0000000a
00470EE6 8BC6 mov eax,esi
00470EE8 8B18 mov ebx,[eax]
00470EEA FF530C call dword ptr [ebx+$0c]
Run Code Online (Sandbox Code Playgroud)
UPDATE
上面的代码由Delphi 2009编译器生成.您可以使用{$ STRINGCHECKS OFF}指令改进代码,但仍然有UniqueStringU函数调用开销:
Unit7.pas.100: Stream.Read(s[1], 10);
00470EB8 8D45FC lea eax,[ebp-$04]
00470EBB E8B850F9FF call @UniqueStringU
00470EC0 8BD0 mov edx,eax
00470EC2 B90A000000 mov ecx,$0000000a
00470EC7 8BC3 mov eax,ebx
00470EC9 8B18 mov ebx,[eax]
00470ECB FF530C call dword ptr [ebx+$0c]
Run Code Online (Sandbox Code Playgroud)
第二个选项肯定是更多的"Delphi风格"(如果你看一下Windows API头文件的Delphi版本,你会看到大多数指针参数已经转换为var参数).
除此之外,第二个选项不需要强制转换,并且更易读恕我直言.
我总是使用第二个保持类型安全的.我真的没有购买性能参数,因为你最糟糕的情况是打磁盘,或文件缓存或主内存,所有这些都会让一些CPU操作看起来有些微不足道.正确性应优先于绩效.
但是,我想补充一点,这不应该让你烦恼太多,因为你应该只编写一次这段特殊的代码.把它放在一个帮助器类中并将其包好.随意关注优化,将其重新编写为汇编程序,无论您喜欢什么.但d on't [R EPEAT ÿ我们自己.