Delphi,,,也许是老了
我一直对Delphi的编译质量心存疑虑,但是因为一直做的项目都对性能要求不高,就没多在意,直到前段d7的update1,编译器有所改动,应用中感觉得到,有改进的地方,也有不足的地方,自己心想,不足的地方绕开就是了,也没什么大不了,事情到了前些天,在调试一段代码的过程中隐隐感觉Delphi好像愚蠢了好多,传递几个参数,push上去,又pop回来,不过也算了,毕竟要不了多少个时钟,也可能Delphi一直就这样,依然是没太在意,昨天看
http://community.csdn.net/Expert/topic/3329/3329170.xml?temp=.6715814
中reallike兄弟贴的汇编码,我也动手察看了一下,关掉debug选项,然后反编译看了一下,的确是这样,可能是版本不同的缘故,我这边的结果而且比reallike贴的更加烦复,我盯着结果硬是愣了好半分钟,我对Delphi的编译器产生了更强烈的怀疑!我再急忙再打开伟大的Delphi 5,这才发现过去熟悉的简洁的编译结果,,,,,,
自从前几年GNU和MS,intel等新版的高度优化的编译器出现以后,我也一直断断续续的在编码工作过程中比较它们,以前也发过几个贴提过Delphi不足的地方,可是我万没想到,在编译器技术登峰造极的今天,Delphi 7.1编译出来的竟是如此Keep It Simple Stupid!
也许是给CLX/Kylix/.net弄了几年,疲于应付各种语法变动和适应不同环境的要求,Delphi的编译质量已大不如前,,,
Delphi,我这位伟大的老师,您也许是老了,,,
问题点数:200、回复次数:68Top
1 楼fenger8293(http://www.pceggs.cn/pgComDefault.aspx?ID=191016)回复于 2004-09-02 11:44:58 得分 4
1楼Top
2 楼qiujsh(www.chinascsoft.com)回复于 2004-09-02 11:46:06 得分 4
delphi确实版本越高编译出的程序越大,运行效率越低Top
3 楼qxj(己所不欲,勿施于人;送人玫瑰,手有余香!)回复于 2004-09-02 11:47:52 得分 3
呵呵,Top
4 楼luke5678()回复于 2004-09-02 11:48:14 得分 3
老有老的味道!Top
5 楼wofan(我烦)回复于 2004-09-02 11:51:06 得分 3
姜是老的辣呀Top
6 楼juliens(星星球愛思纯^_^)回复于 2004-09-02 11:57:17 得分 3
楼上两位正解!Top
7 楼minquan2001(鸣泉)回复于 2004-09-02 12:08:33 得分 3
delphi7.0在编译的时候确实是太容易出错了,效率也很低。Top
8 楼linzhengqun(风。我回来了)回复于 2004-09-02 12:16:21 得分 3
呵呵,热情不变。Top
9 楼Jeff20040819(美丽)回复于 2004-09-02 12:16:49 得分 3
应该还不错把Top
10 楼operfume(橘子香水)回复于 2004-09-02 12:20:08 得分 3
<input type="button" value="说得对">Top
11 楼ghy412(用心良苦)回复于 2004-09-02 12:27:10 得分 3
无奈~Top
12 楼dyzg(地藏)回复于 2004-09-02 12:32:02 得分 3
呵呵,接分Top
13 楼windy2008(だ青苹果だ)回复于 2004-09-02 12:37:36 得分 3
还是不错的.
他们都说的对啊.
老是老了点,但是还是有很大的用处啊.
不要再怀疑了.Top
14 楼zfang(真爱无敌)回复于 2004-09-02 12:40:55 得分 3
路过
Top
15 楼yinweixian(blackyin)回复于 2004-09-02 12:44:06 得分 3
可能!Top
16 楼getit911(Windows转Linux中)回复于 2004-09-02 12:48:07 得分 3
现在需要Delphi解决的问题,比D5时代要复杂N倍,而硬件能力要比D5时代要强大N倍,所以做些调整也是正常的。Top
17 楼fim(阿初)回复于 2004-09-02 12:48:09 得分 3
支持一下,接分Top
18 楼zjqyb(风清扬*任它溺水三千,我只取一瓢饮*)回复于 2004-09-02 12:50:52 得分 3
delphi5,delphi6下
Result := PPointer(Self)^;
均为:
mov eax,[eax]
没有其他
Top
19 楼loader(追求必将永恒!)回复于 2004-09-02 13:13:34 得分 3
怀念D5那个一闪而过得编译效果,太爽了Top
20 楼jerryf(object Sender)回复于 2004-09-02 14:04:46 得分 3
d7啊d7,让我欢喜让我愁啊Top
21 楼Sunniness(逛追->理想)回复于 2004-09-02 14:26:03 得分 3
Delphi 现在千万不要老,我才开始学!
支持!Top
22 楼pandengzhe(无为之为 之 混迹苍生)回复于 2004-09-02 14:38:05 得分 3
......Top
23 楼GoldShield(李柏岑)回复于 2004-09-02 14:43:23 得分 3
我.?.没什么说的!
Top
24 楼2312(╰@oo恒星★)回复于 2004-09-02 14:58:25 得分 3
我几乎不用d7Top
25 楼dyzg(地藏)回复于 2004-09-02 15:08:28 得分 3
版本越高,包容的东西越多,本是无可厚非,编译复杂了一些,对于现在的硬件发展速度来说不算啥,编译后的代码速度,简洁程度能否接受关键是要看用户,而不是编程发烧友。Top
26 楼Shiyl(云淡风清 卷舒自在)回复于 2004-09-02 15:11:51 得分 3
原来如此
----------------------------------------------------------------
花自飘零水自流,一种相思,两处闲愁。
此情无计可消除,才下眉头,又上心头。
----------------------------------------------------------------
Top
27 楼FrameSniper(http://naoku.net/blogs/framesniper/)回复于 2004-09-02 15:13:05 得分 3
呵呵,这里有没有对编译原理非常熟悉的人啊?Top
28 楼jiang5460(巴山夜雨)回复于 2004-09-02 15:21:52 得分 3
期待Delphi9的出现!!!!Top
29 楼LightJie(闪电)回复于 2004-09-02 15:32:14 得分 3
我是从6开始用的,到7也还好呀!
版本升级,内容变更也是常理,没有什么可以指点的!Top
30 楼boatzm(晓舟怕麻烦)【IUnKnown】(#_#!)回复于 2004-09-02 15:54:29 得分 3
……Top
31 楼notchy(notchy)回复于 2004-09-02 16:06:40 得分 3
最后一个拿分Top
32 楼ly_liuyang(Liu Yang LYSoft http://lysoft.7u7.net)回复于 2004-09-02 16:20:57 得分 3
工具可以变,语言可以变,但算法和思路是不那么容易变的:)Top
33 楼wendy1208(丰)回复于 2004-09-02 16:22:43 得分 3
哎,原来以为开发平台有多强大,可是才用了不久,就出现很多bug,真是像 jerryf() 说的“d7啊d7,让我欢喜让我愁啊”Top
34 楼zdq801104(【☆这个杀手不太冷☆】)回复于 2004-09-02 16:32:56 得分 3
哎!想转行学别的了Top
35 楼lzy6204(为了忘却的记忆)回复于 2004-09-02 16:56:44 得分 3
用意ly_liuyang(Liu Yang)的观点Top
36 楼kenzone()回复于 2004-09-02 17:07:01 得分 3
各有千秋罢了,D有D的特点Top
37 楼GoldShield(李柏岑)回复于 2004-09-02 17:45:12 得分 3
男人是越老越有味.DELPHI呢? 期待ing....Top
38 楼leeon868(Delphi无神论者)回复于 2004-09-02 17:52:44 得分 3
你不能这么说,如果你这么说你就错了。
在那里我只是说编译的机械了点,并不代表编译器很差劲!
我前一阵之搞了一个字符是数字的汇编代码。
我写了一些之后,突然想知道delphi编译后是什么效果。
最后我信服了delphi的编译器。
如果我写,我会用cmp,然后跳转。
win32汇编如下:
.const
zero db 48
nine db 57
IsNum1 proc _char
mov eax, _char
cmp al, zero
jb @@NotIsNum
cmp al, nine
jbe @@IsNum
@@NotIsNum:
xor eax, eax
ret
@@IsNum:
mov al, 01
IsNum1 endp
但是delphi信服的让我知道了什么叫做真正强,真正的代码优化:
IsNum2 proc _char
mov eax, _char
add al, 208
sub al, 10
setb al
IsNum2 endp
这才是delphi……
有些时候是不一样的,为了保证一些东西。Top
39 楼leeon868(Delphi无神论者)回复于 2004-09-02 18:00:33 得分 3
或许在今天,需要把开发门槛降低,在解析的过程中,自然有了一系列保证措施。
而且,随着Cpu速度的不断加快,几个时钟频率,那微乎其微。保证运行才是前提。
现在并不是所有人都把Cpu, Fpu窗口打开,检查有什么异常,并不是所有人都用softice……
唉,老兄其实没有必要赞叹这个,毕竟编译器的高手Anders去了微软……Top
40 楼leeon868(Delphi无神论者)回复于 2004-09-02 18:05:32 得分 3
又如,原来PChar的变量是不能被string直接赋值的,得用函数。
delphi4好像不行,而现在,直接用:=就可以了。编译器作了许多类型检查工作。
有些类型检查多了,自然就机械了,毕竟编译器不是人。Top
41 楼jiayodo(爱上一只猪)回复于 2004-09-02 18:16:09 得分 3
delphi不错,我一直在用它Top
42 楼rcaicc(√(没完没了))回复于 2004-09-02 19:14:08 得分 3
还没怎么学呢就老了啊。Top
43 楼smiler007(笑一笑)回复于 2004-09-02 19:41:11 得分 3
没什么的,不是问题的问题Top
44 楼zhangtao133121(布列瑟侬)回复于 2004-09-02 20:12:59 得分 3
d7编译确实是太容易出错了,效率也很低。
Top
45 楼lw549(那个孩子他爹)回复于 2004-09-02 21:06:34 得分 3
我只在乎结果,能运行就ok,没必要斤斤计较
如果在效率要求苛刻的场合,可以用汇编。Top
46 楼longtusoft(神灯之主)回复于 2004-09-02 21:31:02 得分 3
不过,现在的机器也越来越快了,得想办法让它慢下来!Top
47 楼lnjasmine(呵呵)回复于 2004-09-02 21:42:42 得分 3
111111111111111111111111111111111111111Top
48 楼netcrawller(放弃绑定-不用第三方控件)回复于 2004-09-02 21:56:00 得分 3
不用D7Top
49 楼reallike(爱翔)(学得太多,得休息一下)回复于 2004-09-03 10:01:01 得分 3
其实没有再多可说得了,现在没有必要关心这些细节性的问题,效率性的问题。这不是大方向。delphi提供了什么,我们就用什么。Top
50 楼huwei001982(凶猛的小狗)回复于 2004-09-03 10:34:28 得分 3
过时了, 控件堆起来的!Top
51 楼rockswj(石头,一直再努力)回复于 2004-09-03 11:07:44 得分 3
接分吧。
------------------------------------
支持CSDN's forum ExplorerTop
52 楼alphax(豪言壮语的乌鸦)回复于 2004-09-03 12:42:04 得分 0
来了这么多兄弟,应该是我发的贴子中最热的贴子了
先说明一下,有些兄弟误解我的意思了,我的意思不是说Delphi没用了,Delphi的优势一直不在编译质量,它的优势在VCL和他的编译速度,当然编译具有一定的质量那也是一个前提。
我猜Delphi是一遍编译的,毕竟一遍编译要想优化得很好,公认还是很难的,因此可以说Delphi 5已经做得很不错了
只是Delphi 7确实比Delphi 5差远了,很多多余的代码,寄存器分配和利用也很差劲,
象TObject.ClassType()的代码,我实际得到的是
$004034B4 PUSH ECX 1
$004034B5 MOV EAX,[EAX] 2
$004034B7 MOV [ESP],EAX 3
$004034BA MOV EAX,[ESP] 3
$004034BD POP EDX 1
$004034BE RETN 1
D5的很简单,两句话,3个字节,前面的zjqyb已经贴过了:
mov eax, [eax]
ret
好的代码生成的算法无须额外的窥孔优化就可以避免像多余的载入和保存这样的问题
另外,想问问reallike,你的那段IsNum2代码是在什么版本下和什么情况下产生的?如果我没有猜错,这应该是超级编译得到的结果,这种优化需要额外的时间来进行模式匹配,Delphi还有一些比较基本的优化都没有做,会做这种优化?你是骗我的吧
我用D7.1编译以下函数:
function IsNum2(const aChar: Char): Boolean;
begin
Result := (aChar >= '0') and (aChar <= '9');
end;
function IsNum3(const aChar: Char): Boolean;
begin
Result := not ((aChar > '9') or (aChar < '0'));
end;
还有一个在循环中检查字符是否数字的,编译结果基本上都是以下这样的,只是cmp的位置安排稍稍不同:
$0045C414 CMP DL,30 3
$0045C417 JB SHORT 0045C41E 2
$0045C419 CMP DL,39 3
$0045C41C JBE SHORT 0045C421 2
$0045C41E XOR EAX,EAX 2
$0045C420 RETN 1
$0045C421 MOV AL,1 2
$0045C423 RETN 1
并不是象reallike说的那样
我还测试了复杂一点点的例子,
const
BitIsAlpha = $01;
BitIsDigit = $02;
BitIsIdentFirstChar = $04;
BitIsIdentSeqChar = $08;
BitIsHex = $10;
MaskIsAlphaDigit = BitIsAlpha or BitIsDigit;
const
TokenConvertTable: array[Char] of Byte
= (
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$1A, $1A, $1A, $1A, $1A, $1A, $1A, $1A,
$1A, $1A, $00, $00, $00, $00, $00, $00,
$00, $1D, $1D, $1D, $1D, $1D, $1D, $0D,
$0D, $0D, $0D, $0D, $0D, $0D, $0D, $0D,
$0D, $0D, $0D, $0D, $0D, $0D, $0D, $0D,
$0D, $0D, $0D, $00, $00, $00, $00, $04,
$00, $1D, $1D, $1D, $1D, $1D, $1D, $0D,
$0D, $0D, $0D, $0D, $0D, $0D, $0D, $0D,
$0D, $0D, $0D, $0D, $0D, $0D, $0D, $0D,
$0D, $0D, $0D, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00
);
function IsAllDigit(const aDigitStr: string): Boolean;
{
检查一个字符串是否全部都是数字
}
var
P: PChar;
begin
P := Pointer(aDigitStr);
Result := False;
if P <> nil then
begin
repeat
if TokenConvertTable[P^] and BitIsDigit = 0 then Exit;
Inc(P);
until P^ = #0;
Result := True;
end;
end;
D 7.1的结果(已经关闭所有debug选项,保持O+状态),总共42 bytes:
------------------------------------------
$0045BAD8 PUSH ECX 1
//只是为了获得一个栈上临时变量的空间
$0045BAD9 MOV [ESP],EAX 3
//竟然是为了腾出EAX,,,整个函数中,ECX象个傻子那样站着不动
$0045BADC XOR EAX,EAX 2
//以下,就一直用栈的变量来操作
$0045BADE CMP DWORD PTR [ESP],0 4
$0045BAE2 JE SHORT $0045BB00 2
$0045BAE4 MOV EDX,[ESP] 3
$0045BAE7 MOVZX EDX,BYTE PTR [EDX] 3
//把栈上存的地址拿出来,再取得字符扩展成dword
$0045BAEA TEST BYTE PTR [EDX+$45EFF8],2 7
$0045BAF1 JE SHORT $0045BB00 2
$0045BAF3 INC DWORD PTR [ESP] 3
//3个字节,,,没法子,因为开头的安排就造成了这样的结果
$0045BAF6 MOV EDX,[ESP] 3
$0045BAF9 CMP BYTE PTR [EDX],0 3
//和前面一样,,,
$0045BAFC JNZ SHORT $0045BAE4 2
$0045BAFE MOV AL,1 2
$0045BB00 POP EDX 1
//恢复栈指针
$0045BB01 RETN 1
D5的结果(没有关闭debug选项, $O+),可以说是非常精简(30 bytes)而且也容易理解的, 犹如当年吕不韦大大主编的吕氏春秋:
------------------------------------------
+$00 xor edx, edx
+$02 test eax, eax
+$04 jz IsAllDigit + $1B
+$06 xor ecx, ecx
+$08 mov cl, [eax]
+$0A test byte ptr [ecx+TokenConvertTable], $02
+$11 jz IsAllDigit + $1B
+$13 inc eax
+$14 cmp byte ptr [eax], $00
+$17 jnz IsAllDigit + $06
+$19 mov dl, $01
+$1B mov eax, edx
+$1D ret
这些例子比较短,不能看出很多问题,但是还是能明显看到冗余加载存储这样以及放着可用寄存器不用却去用栈的现象了,但是还有一些比较复杂的,因为时间关系,我就不贴了,,,使用D7的朋友会发现的
Top
53 楼lichaohui(lich)回复于 2004-09-03 12:51:31 得分 3
Result := Pointer(PPointer(Self)^)
这样写就好了,Delphi虽然支持被赋值的对象进行类型强制转化,但是没有在这方面优化
也许是为了安全性考虑吧,机器代码优化能力降低了
Top
54 楼arraden(小兵传奇)回复于 2004-09-03 12:58:42 得分 3
用Top
55 楼yestoyes(枫)回复于 2004-09-03 13:09:40 得分 3
顶Top
56 楼reallike(爱翔)(学得太多,得休息一下)回复于 2004-09-03 13:15:58 得分 3
ECX保留作循环寄存器,一般是不会直接使用的。这个可以理解。
也或许做其他事情,真的非常非常少见用ecx,
其次少用的是ebx,有没有看到,你的代码里,没有出现ebx,这个我百思不得其解……
常用就是edx加指针寄存器,或者直接用堆栈。
栈用的非常频繁。感觉他们好像尽量不使用寄存器,宁愿开辟一块新的栈。
d5编译的确实精简。
到底什么迫使他们做了这些呢?两个寄存器几乎都不用偏偏用栈。
唉…… 对我们来说,没有答案……Top
57 楼reallike(爱翔)(学得太多,得休息一下)回复于 2004-09-03 13:19:40 得分 3
Ehom说在Delphi中用EBX保存Self指针。这个也可以理解…… 唉……
但,我认为,宁愿把self先放到栈里面,然后再用,再恢复,也比直接用栈来的好。
ecx在做什么呢?几乎不用?Top
58 楼reallike(爱翔)(学得太多,得休息一下)回复于 2004-09-03 13:27:10 得分 3
但不确定的是…… 李维在Inside vcl中说:Self是存在eax寄存器中,而且是有“历史的”,且,那个,Result := PPointer(Self)^ 清晰地解释了这个,不过我也信Ehom说的。但不知道是在什么情况下使用ebx保存self指针。
唉…… 不明白啊。问大哥去……Top
59 楼leeon868(Delphi无神论者)回复于 2004-09-03 13:34:28 得分 3
我的那个IsNum2是这么写的:
function IsNum2(const aChar: Char): Boolean;
begin
Result := aChar in ['0'..'9'];
end;
Unit1.pas.28: Result := aChar in ['0'..'9'];
00454700 04D0 add al,$d0
00454702 2C0A sub al,$0a
00454704 0F92C0 setb al
Unit1.pas.29: end;
另外,别用反汇编了,不如debug窗口。
http://www.cnvcl.org/showdetail.php?id=276
下载一个CnWizard,就可以复制cpu窗口的汇编代码了。
aminigoo写的,YYGW老大搞进去的。
:〉Top
60 楼Eastunfail(龍子龍孫) (Serpent's Embrace)回复于 2004-09-03 13:38:39 得分 3
也许Borland在考虑现在硬件越来越快速,对于性能的要求没有必要依赖编译器(若真的要高性能还不如使用汇编或者Intel的编译器,Intel做的优化更强)了。
在我印象中,编译器生成的一些中间代码如果大量使用栈,而很少或者不用寄存器,那是为了跨CPU。也许从D7开始,D7使用的编译器前端和Delphi.net用的编译器的前端是一样的。Top
61 楼reallike(爱翔)(学得太多,得休息一下)回复于 2004-09-03 13:38:48 得分 3
从你给出的反汇编代码发现,编译器对ecx小心翼翼,Push ecx几乎都有。何故?
老兄的测试实在让人叹服…… 唉,我的测试没有这么全面……Top
62 楼Eastunfail(龍子龍孫) (Serpent's Embrace)回复于 2004-09-03 13:39:07 得分 3
小子见解,不成气候Top
63 楼reallike(爱翔)(学得太多,得休息一下)回复于 2004-09-03 13:49:34 得分 3
唉,不可理解的用栈来操作,时钟周期是3。
对寄存器操作可是1啊。Top
64 楼Eastunfail(龍子龍孫) (Serpent's Embrace)回复于 2004-09-03 13:50:58 得分 3
如果要生成高效的代码,那就不要用Delphi。用Intel的FORTRANTop
65 楼Eastunfail(龍子龍孫) (Serpent's Embrace)回复于 2004-09-03 13:57:26 得分 3
Delphi的长处并不是生成高效的代码。所以没有必要在这方面来匹配Delphi。
速度能被人接受就可以了,毕竟极大提高程序运行速度靠的不是编译器和平台(要不然Java和.net是吃白饭的?),架构、算法、设计才是王道Top
66 楼reallike(爱翔)(学得太多,得休息一下)回复于 2004-09-03 14:04:43 得分 3
嗬嗬,小眼睛不要这么说。
毕竟楼顶的老兄是在赞叹delphi版本搞了,编译效果反而差劲了。Top
67 楼chengangcsdn(wenxin)回复于 2004-09-03 14:21:56 得分 3
我相信大家都看过Borland传奇吧!
若没有看过的话。是值得一看的!
也许正如李维所说,是由于Borland的产品线太长了的原因吧!
期待Delphi9!!!!!!!!!!!!!!!
Top
68 楼alphax(豪言壮语的乌鸦)回复于 2004-09-03 18:47:25 得分 0
to reallike,
谢谢了,原来Delphi对in操作符的优化还是挺好的,我还一直避免使用in嘞
还谢谢你的cnwizard推荐
to Eastunfail,
--编译器生成的一些中间代码如果大量使用栈,而很少或者不用寄存器,那是为了跨CPU。
你说的有道理,Top




