问:TString 与TStringList在D7和D2006下(100分)

  • 主题发起人 主题发起人 xminsong
  • 开始时间 开始时间
X

xminsong

Unregistered / Unconfirmed
GUEST, unregistred user!
TString 与TStringList在D7和D2006下
Const
bbq=1000000;
bb='HHHHHHHHH';

一、 for I := 1 to bbq do //TStringList
L.Add(bb);

二、 for I := 1 to bbq do //String
S:=S+bb+#13#10;

在D7中“一 ”比“二”快,在D2006中“二”比“一”快,但同一类型D2006比D7快。
本人是菜鸟,哪位大侠能讲解一下?
 
在D7下由于String在每次复新赋值的时候都会重新分配内存,所以在上面的例子里会存在"一"比"二"快
在D2006中实际上String对应的是System.String是CTS中的基本类型,所以比TStringList类运算起来要快
 
谢谢Beyondbill大侠指点,明天结账![:)]
 
这个解释牵强得很。我的测试报告如下:

TStringList Loop: 1000000 ElapsedTime: 594
TStringList Loop: 1000000 ElapsedTime: 500
TStringList Loop: 1000000 ElapsedTime: 547
TStringList Loop: 1000000 ElapsedTime: 437
TStringList Loop: 1000000 ElapsedTime: 469
TStringList Loop: 1000000 ElapsedTime: 437
TStringList Loop: 1000000 ElapsedTime: 406
TStringList Loop: 1000000 ElapsedTime: 500
TStringList Loop: 1000000 ElapsedTime: 485
TStringList Loop: 1000000 ElapsedTime: 437
TStringList Loop: 1000000 ElapsedTime: 438
TStringList Loop: 1000000 ElapsedTime: 516
TStringList Loop: 1000000 ElapsedTime: 469
TStringList Loop: 1000000 ElapsedTime: 531

String Loop: 1000000 ElapsedTime: 437
String Loop: 1000000 ElapsedTime: 375
String Loop: 1000000 ElapsedTime: 406
String Loop: 1000000 ElapsedTime: 344
String Loop: 1000000 ElapsedTime: 375
String Loop: 1000000 ElapsedTime: 391
String Loop: 1000000 ElapsedTime: 391
String Loop: 1000000 ElapsedTime: 375
String Loop: 1000000 ElapsedTime: 422
String Loop: 1000000 ElapsedTime: 484
String Loop: 1000000 ElapsedTime: 422
String Loop: 1000000 ElapsedTime: 422
String Loop: 1000000 ElapsedTime: 359
String Loop: 1000000 ElapsedTime: 422

测试代码如下:
const
bbq = 1000000;
bb = 'HHHHHHHHH';

procedure TForm1.Button1Click(Sender: TObject);
var
t1, t2: DWord;
L: TStringList;
i: integer;
begin
L := TStringList.Create;
t1 := GetTickCount;
for I := 1 to bbq do //TStringList
L.Add(bb);
t2 := GetTickCount;
L.Free;
Memo1.Lines.Add(Format('TStringList Loop: %d ElapsedTime: %d', [bbq, t2 - t1]));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
t1, t2: DWord;
S: string;
i: integer;
begin
t1 := GetTickCount;
for I := 1 to bbq do // String
S := S + bb + #13#10;
t2 := GetTickCount;

Memo1.Lines.Add(Format('String Loop: %d ElapsedTime: %d', [bbq, t2 - t1]));
end;

结论正好和楼主相反。我是Delphi7 ,代码优化:“关闭”。
TStringList 在使用 ADD 方法时也是随用随分配内存,TStringList 比 String 还多了一
个检查容量和检查排序等代码,不会比 String 快的。
 
[:)]谢谢小雨哥,试试先!
 
to:小雨哥
我的电脑配置:塞杨D2.13,768内存,WinXP-SP1(Home版)
结果如下:
D7:
TStringList Loop: 1000000 ElapsedTime: 438
TStringList Loop: 1000000 ElapsedTime: 438
TStringList Loop: 1000000 ElapsedTime: 437
TStringList Loop: 1000000 ElapsedTime: 437
TStringList Loop: 1000000 ElapsedTime: 438

String Loop: 1000000 ElapsedTime: 515
String Loop: 1000000 ElapsedTime: 485
String Loop: 1000000 ElapsedTime: 516
String Loop: 1000000 ElapsedTime: 484
String Loop: 1000000 ElapsedTime: 500
TStringList比String快

D2006:
TStringList Loop: 1000000 ElapsedTime: 235
TStringList Loop: 1000000 ElapsedTime: 234
TStringList Loop: 1000000 ElapsedTime: 250
TStringList Loop: 1000000 ElapsedTime: 265
TStringList Loop: 1000000 ElapsedTime: 250

String Loop: 1000000 ElapsedTime: 235
String Loop: 1000000 ElapsedTime: 203
String Loop: 1000000 ElapsedTime: 235
String Loop: 1000000 ElapsedTime: 187
String Loop: 1000000 ElapsedTime: 203
String比TStringList快点
 
呵呵。我是双通道 P4C 、Windows2003 SP1。可能与操作系统有关。
我优化串存取后的结果如下,TStringList 无法优化存取。

String Loop: 1000000 ElapsedTime: 94
String Loop: 1000000 ElapsedTime: 94
String Loop: 1000000 ElapsedTime: 94
String Loop: 1000000 ElapsedTime: 46
String Loop: 1000000 ElapsedTime: 78
String Loop: 1000000 ElapsedTime: 94
String Loop: 1000000 ElapsedTime: 62
String Loop: 1000000 ElapsedTime: 63
String Loop: 1000000 ElapsedTime: 62
String Loop: 1000000 ElapsedTime: 93
String Loop: 1000000 ElapsedTime: 78
String Loop: 1000000 ElapsedTime: 78
String Loop: 1000000 ElapsedTime: 93
String Loop: 1000000 ElapsedTime: 93
 
[:)]很高兴见到小雨哥的回答!
决定明晚结账,不想拖,近期比较忙,怕忘了结贴。
有空我再去找一找资料。
 
我在虚拟机的 XP Pro SP2 上测试的相同代码结果如下:

TStringList:

TStringList Loop: 1000000 ElapsedTime: 469
TStringList Loop: 1000000 ElapsedTime: 469
TStringList Loop: 1000000 ElapsedTime: 437
TStringList Loop: 1000000 ElapsedTime: 453
TStringList Loop: 1000000 ElapsedTime: 454
TStringList Loop: 1000000 ElapsedTime: 453
TStringList Loop: 1000000 ElapsedTime: 453
TStringList Loop: 1000000 ElapsedTime: 469

String:

String Loop: 1000000 ElapsedTime: 281
String Loop: 1000000 ElapsedTime: 297
String Loop: 1000000 ElapsedTime: 313
String Loop: 1000000 ElapsedTime: 312
String Loop: 1000000 ElapsedTime: 360
String Loop: 1000000 ElapsedTime: 297
String Loop: 1000000 ElapsedTime: 297
String Loop: 1000000 ElapsedTime: 297

String 存取优化:

String Loop: 1000000 ElapsedTime: 78
String Loop: 1000000 ElapsedTime: 78
String Loop: 1000000 ElapsedTime: 63
String Loop: 1000000 ElapsedTime: 79
String Loop: 1000000 ElapsedTime: 78
String Loop: 1000000 ElapsedTime: 78
String Loop: 1000000 ElapsedTime: 78
String Loop: 1000000 ElapsedTime: 78
 
String存取是如何优化的
望赐教
 
所谓的存取优化就是尽可能地一次性分配内存,避免逐次申请,以加快操作。
 
各位再试着把bbq = 10000000再试一次,肯定是String要比TStringList慢得多。确实D7使用了所谓的存取优化,每次分配一个String变量的时候都会分配足够大的空间,在有限的String长度进行String的连接运算是看不出TStringList比它快,甚至还会出现稍慢的情况,但超过了那个长度,肯定是要慢得多的,它会再重新分配一个大空间,而这个分配时间就需要一个比较长的时间,它会首先申请一个更大的空间,然后把以前的数据再移到新空间
 
在我的机器上,在bbq=10000000上两者之间运算的差别实在是很远,一个在16400多毫秒,一个只在3250~4200毫秒左右,虽然只是相对以前的bbq值放大了十倍,但String本身问题的不足马上就暴露无疑
 
我加了10倍循环后的结果如下:

TStringList Loop: 10000000 ElapsedTime: 3094
TStringList Loop: 10000000 ElapsedTime: 2953
TStringList Loop: 10000000 ElapsedTime: 2953
TStringList Loop: 10000000 ElapsedTime: 2969
TStringList Loop: 10000000 ElapsedTime: 2953
TStringList Loop: 10000000 ElapsedTime: 2953
TStringList Loop: 10000000 ElapsedTime: 2969
TStringList Loop: 10000000 ElapsedTime: 2969
TStringList Loop: 10000000 ElapsedTime: 2984
TStringList Loop: 10000000 ElapsedTime: 2985

String Loop: 10000000 ElapsedTime: 2578
String Loop: 10000000 ElapsedTime: 2562
String Loop: 10000000 ElapsedTime: 2562
String Loop: 10000000 ElapsedTime: 2579
String Loop: 10000000 ElapsedTime: 2562
String Loop: 10000000 ElapsedTime: 2578
String Loop: 10000000 ElapsedTime: 2562
String Loop: 10000000 ElapsedTime: 2578
String Loop: 10000000 ElapsedTime: 2562

从结果看,在我的机器上还是 String 快。
 
不过,有一点要说明的,就是测试的时候,CPU 不同结果会相差很大,因为 TStringList
在内部已经做了人工优化,可以不完全依赖 CPU 的性能,String 没有优化,对 CPU 的快
速缓存依赖比较大,如果让 String 存取也人工优化一下,免除 CPU 的依赖的结果如下:
String Loop: 10000000 ElapsedTime: 532
String Loop: 10000000 ElapsedTime: 531
String Loop: 10000000 ElapsedTime: 531
String Loop: 10000000 ElapsedTime: 531
String Loop: 10000000 ElapsedTime: 531
String Loop: 10000000 ElapsedTime: 531
String Loop: 10000000 ElapsedTime: 531
String Loop: 10000000 ElapsedTime: 531
String Loop: 10000000 ElapsedTime: 531
String Loop: 10000000 ElapsedTime: 515
 
既然你的机器有这么快,那你就再加10倍试试,再怎么优化也改变不了String在每次超过本身分配内存长度会重新分配内存,导致不如TStringList这个本质
 
有一点,CPU不同,好像导致的结果也确实存在差异。楼主之所以在bbq=1000000的情况下就会出现TstringList比String快的情况与小雨哥你相反,包括我的也是,这都和CPU有相当大的关系
 
同意小雨哥的解释。顺便问一下,你是用什么工具或方法得到ElapsedTime的?因为我也想测一下我程序的性能。
To Beyondbill:
其实string这种数据类型本身很简单,就是一个指针加上它前面的两个32位的管理数据。编译器本身并没有对这种数据类型做多少所谓的“存取优化”,正好相反,由于copy on write机制的存在,它还可能在程序员不知情的情况下降低性能。
而小雨哥所说的“尽可能地一次性分配内存,避免逐次申请,以加快操作”的存取优化技术并不是指编译器本身对这种数据类型提供的,而是程序员“自己”考虑的问题。
以楼主所举的代码二为例--
for I := 1 to bbq do //String
S:=S+bb+#13#10
这是原来没有优化的代码,如果大家稍微注意一下就可以发现:由于在整个字符串循环连接过程中,bb的内容(或者说长度)是不变的,那在循环次数确定的情况下,我们知道最终字符串S的长度其实在循环开始(也就是开始连接字符串)之前就是已经确定了的。因此这段代码可以用预分配字符串长度的方法来优化--
// 优化方案一:
SetLength(S,bbq*(Length(bb)+2));
j:=0;
for i:=1 to bbq do
begin
for k:=1 to Length(bb) do
begin
inc(j);
S[j]:=bb[k]
end;
S[j+1]:=#13;
S[j+2]:=#10;
j:=j+2
end

基本上,代码写成这样的话,string数据类型的性能潜力已经发挥得差不多了。不过,让我们来看看Delphi帮助里面对string数据类型的一段描述,原文我就不给出了,大意是:“如果用索引的方式(即S:='A')这种方式来写字符串的话,也会引发copy on write机制的作用”。这句话的意思是说,如果有字符串S:='abc'的话,那执行S[1]:='A'赋值语句的时候,会自动生成原字符串'abc'的一个副本。因此为了避开copy on write机制,上面的优化方案一还可以再改一下--
// 优化方案二:
var
S:string;
ps:PChar;
i,j,k:integer;
begin
SetLength(S,bbq*(Length(bb)+2));
ps:=Pointer(S);
j:=0;
for i:=1 to bbq do
begin
for k:=1 to Length(bb) do
begin
ps[j]:=bb[k];
inc(j);
end;
ps[j]:=#13;
ps[j+1]:=#10;
j:=j+2
end
end

请小雨哥和Beyondbill两位测一下,经过这样子优化的程序不论循环次数加大到多少,性能都绝对要比用TStringList要好。
 
我先做一下 Beyondbill 的建议,再加大 10 倍,结果是:

TStringList Out of memory // 完蛋!超出 TStringList 的容量了

String Loop: 100000000 ElapsedTime: 28672 // 还好,我内存够大,迟早还是能看到结果
 
有趣,用了小雨哥上面的代码,测试结果和小雨哥一致。
TStringList Loop: 1000000 ElapsedTime: 511
TStringList Loop: 1000000 ElapsedTime: 531
TStringList Loop: 1000000 ElapsedTime: 521
TStringList Loop: 1000000 ElapsedTime: 531
TStringList Loop: 1000000 ElapsedTime: 531
String Loop: 1000000 ElapsedTime: 380
String Loop: 1000000 ElapsedTime: 391
String Loop: 1000000 ElapsedTime: 380
String Loop: 1000000 ElapsedTime: 391
String Loop: 1000000 ElapsedTime: 390
 
后退
顶部