这里有比 <a href="http://202.120.85.61/delphibbs/DispQ.asp?LID=283208">世界上最快的替

  • 主题发起人 Another_eYes
  • 开始时间
A

Another_eYes

Unregistered / Unconfirmed
GUEST, unregistred user!
这里有比 <a href="http://202.120.85.61/delphibbs/DispQ.asp?LID=283208">世界上最快的替换函数</a>更快的替换函数(我写的) (0分)<br />Type
TFastPosProc = function (TagStr, SrcStr: PChar;
TagCount: Integer;
var SrcCount: Integer): Integer;
function FastPos(TagStr, SrcStr: PChar;
TagCount: Integer;
var SrcCount: Integer): PChar;
assembler;
asm
// 进入程序时, TagStr在EAX, SrcStr在EBX, TagCount在ECX
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EBP
MOV ESI, EAX // EAX = TagStr
MOV EDI, EDX // EDX = SrcStr
MOV EDX, SrcCount // EDX= SrcCount
AND EDX, EDX // SrcCount <= 0?
JLE @NotFound // Yes, not found
DEC ECX // 如果第一个字符匹配时 需要比较的字节数
JL @NotFound // TagCount < 0? Yes-> not found
SUB EDX, ECX // else
计算需要搜索的SrcStr长度
// 此长度=SrcCount - TagCount + 1
JLE @NotFound // SrcCount < TagCount ? Yes-> not found
LODSB // AL = TagStr[1] ESI指向下一个字节
MOV AH, CL //保存当前TagStr长度mod 4的尾数
AND AH, 3 // 到AH中
SHR ECX, 1
SHR ECX, 1
MOV EBP, ECX // 按4字节算的长度, 保存在EBP
MOV ECX, EDX // ECX=SrcCount
@StartFind:
REPNZ SCASB // 搜索第一个字符
JNZ @NotFound // 找不到
MOV EDX, ECX // 保存剩余的SrcCount
MOV ECX, EBP // 取得TagStr长度
MOV EBX, EDI //保存当前SrcStr地址
REPZ CMPSD // 按4字节为单位比较比较Src和Tag
JNZ @FindNext // 不相等
XOR ECX, ECX // ECX清0
MOV CL, AH
REPZ CMPSB // 剩下的字节进行比较
JNZ @FindNext // 不相等
// 找到了, 返回找到的地址, 并修改SrcCount变量(已经去掉了匹配字符串的长度)
DEC EDX // 程序开始时多算一个字节, 现在去掉
MOV SrcCount, EDX
MOV EAX, EBX // 匹配起始地址, 未包含第一个字符
DEC EAX // 包含进第一个匹配字符
JMP @Found
@FindNext:
MOV ESI, SrcStr
INC ESI // 恢复参数准备查找下一轮匹配的字符
MOV ECX, EDX
MOV EDI, EBX
JMP @StartFind
@NotFound:
XOR EAX, EAX // 未找到, 返回 0, 并且SrcCount不变
@Found:
POP EBP
POP EBX
POP EDI
POP ESI
end;

function FastPosNoCase(Tag, Src: PChar;
TagCount: Integer;
var SrcCount: Integer): PChar;
assembler;
asm
PUSH ESI
PUSH EDI
PUSH EBX
MOV EDI, EAX // EAX = Tag
MOV ESI, EDX // EDX = Src
MOV EDX, SrcCount
XCHG EDX, ECX // 结果: ECX = SrcCount EDX = TagCount
DEC EDX // 找到第一个字符后需要匹配的个数
JL @NotFound // TagCount <= 0 ?
OR ECX, ECX // SrcCount <= 0 ?
JLE @NotFound
SUB ECX, EDX
JLE @NotFound // SrcCount < TagCount ?
MOV AH, [EDI] // 取第一个字符进行查找
INC EDI
CMP AH, 97
JB @StartScan // < 'a' ?
CMP AH, 122 // > 'z' ?
JA @StartScan
SUB AH, 32 // 转大写
@StartScan:
OR ECX, ECX
JZ @NotFound
LODSB
CMP AL, 97
JB @@1
CMP AL, 122
JA @@1
SUB AL, 32 // 转大写
@@1:
CMP AH, AL
JZ @CmpStr
DEC CX
JNZ @StartScan
JMP @NotFound
@CmpStr:
DEC ECX
MOV EBX, EDX
SHL EAX, 16 // 保存AH
@ContinueCompare:
MOV AH, [ESI + EBX]
CMP AH, 97
JB @@2
CMP AH, 122
JA @@2
SUB AH, 32
@@2:
MOV AL, [EDI+EBX]
CMP AL, 97
JB @@3
CMP AL, 122
JA @@3
SUB AL, 32
@@3:
CMP AH, AL
JNZ @FindNext
DEC EBX
JG @ContinueCompare
@Found:
DEC ECX
MOV SrcCount, ECX
MOV EAX, ESI
DEC EAX
JMP @OutPoint
@NotFound:
XOR EAX, EAX
JMP @OutPoint
@FindNext:
SHR EAX, 16 // 恢复AH
JMP @StartScan
@OutPoint:
POP EBX
POP EDI
POP ESI
end;

function FastReplace(var Target: string;
FindStr: string;
ReplaceStr: string;
CaseSensitive: Boolean = True): Integer;
// Target: 需要进行替换操作的字符串
// FindStr: 要替换的字符串
// ReplaceStr: 替换成
// CaseSensitive: 是否大小写区分, 默认True (速度快)
// 返回值: 共替换了几个字符串
var
MaxCnt: Integer;
// 保存匹配位置缓冲区的大小
MatchPoses: array of Integer;
// 缓冲区, 保存所有匹配的位置
AdjustLen: Integer;
// 最后需要调整Target的大小
n, // 当前匹配位置
l1, // Target的原始长度
l2, // FindStr的长度
l3, // ReplaceStr的长度
l: Integer;
// 当前剩余需要匹配字符数
Proc: TFastPosProc;
// 查找过程(大小写区分用FastPos, 不区分用FastPosNoCase)
begin
Result := 0;
l1 := Length(Target);
l2 := Length(FindStr);
l3 := Length(ReplaceStr);
if (l1 = 0) or (l2 = 0) then
Exit;
AdjustLen := 0;
MaxCnt:=0;
l := l1;
if CaseSensitive then
Proc := @FastPos
else
Proc := @FastPosNoCase;
// 找出所有匹配字符串的位置, 并填到MatchPoses数组中
n := Integer(PChar(Target));
while (n <> 0) do
begin
n := Integer(Proc(PChar(FindStr), Ptr(n), l2, l));
if n <> 0 then
begin
if Result >= MaxCnt then
// 超过缓冲区大小?
begin
MaxCnt := MaxCnt + 256;
// 扩展缓冲区
SetLength(MatchPoses, MaxCnt);
end;
MatchPoses[Result] := n + AdjustLen;
// 完成替换后在字符串中的位置
Inc(AdjustLen, l3 - l2);
Inc(Result);
Inc(n, l2);
end;
end;
if Result = 0 then
Exit;
// 未找到匹配
if AdjustLen > 0 then
// 新字串比老字串长
begin
SetLength(Target, l1 + AdjustLen);
// 分配内存
// 因为新字串比老字串长
// 因此复制字串时将从尾部开始
asm
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EBP // 保存现场
MOV EAX, ReplaceStr
ADD EAX, L3
DEC EAX // ReplaceStr的结尾地址
MOV EBP, EAX // 保存
MOV EDX, MatchPoses // EDX将指向MatchPoses中最后一个替换
MOV EAX, Result
MOV EBX, EAX // EBX 保存需要替换的次数
DEC EAX
SHL EAX, 1
SHL EAX, 1
ADD EDX, EAX // EDX 定位到MatchPoses[Result-1]
MOV ESI, Target
ADD ESI, L1
DEC ESI // 原Target结尾处
MOV EDI, ESI
ADD EDI, AdjustLen // 新Target结尾处
STD // 设置方向标志: 从尾到头
@@1:
MOV ECX, EDI
LEA EAX, [EDX]
ADD EAX, L3
DEC EAX
SUB ECX, EAX // ECX为两个要替换字符串中间的字符数
MOV EAX, ECX
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, EAX
AND ECX, 3
REP MOVSB // 复制原字符串
MOV EAX, ESI // 保存当前原串位置到EAX
SUB EAX, L2 // 调整位置跳过需要被替换的字符串
MOV ESI, EBP // ESI指向替换成的字符串
MOV ECX, L3 // 长度
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, L3
AND ECX, 3
REP MOVSB // 复制新串到相应位置
SUB EDX, 4 // 下一个要替换的位置
MOV ESI, EAX // 恢复原串位置
DEC EBX
JNZ @@1 // 替换已完成?
// 因为是倒序, 所以第一个需要
// 替换的字符串前的原始串不会
// 改变, 没有必要再进行复制
CLD // 清方向标志
POP EBP
POP EBX
POP EDI
POP ESI // 恢复现场
end;
end
else
if AdjustLen < 0 then
// 因为是缩短Target, 所以先进行
// 替换与复制, 然后再调整内存
begin
asm
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EBP // 保存现场
MOV EAX, ReplaceStr
ADD EAX, L1
ADD EAX, AdjustLen
PUSH EAX // 保存字符串的结尾(为计算最后一次替换
// 后还需要复制多少字节做准备)
MOV EBX, Result
MOV EDX, MatchPoses // EDX指向MatchPoses[0]
LEA ESI, [EDX]
MOV EDI, ESI
JMP @@2 // 第一次替换发生前的部分不需要进行复制
@@1:
LEA ECX, [EDX]
SUB ECX, EDI // 两次替换之间需要复制的字符数
MOV EAX, ECX
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, EAX
AND ECX, 3
REP MOVSB // 复制
@@2:
MOV EAX, ESI
ADD EAX, L2 // 跳过原串中被替换字符串后的位置保存在EAX
MOV ESI, ReplaceStr
MOV ECX, L3
MOV EBP, ECX
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, EBP
AND ECX, 3
REP MOVSB // 替换
MOV ESI, EAX // 恢复ESI到下一次需要移动或替换的位置
ADD EDX, 4 // EDX指向MatchPoses[i+1]
DEC EBX // 所有替换都已经完成?
JNZ @@1
POP ECX
SUB ECX, EDI // 最后一次替换后需要复制的字节数
MOV EBP, ECX
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, EBP
AND ECX, 3
REP MOVSB // 复制
POP EBP
POP EBX
POP EDI
POP ESI // 恢复现场
end;
SetLength(Target, l1 + AdjustLen);
end
else
begin
// 字符串长度不变时
// 只需要进行替换而不必复制了
asm
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EBP
MOV EBX, ReplaceStr //EBX 保存ReplaceStr地址
MOV EDX, MatchPoses
MOV EAX, Result
MOV EBP, L3
@@1:
LEA EDI, [EDX]
MOV ESI, EBX // 比 MOV ESI, ReplaceStr快
MOV ECX, EBP
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, EBP
AND ECX, 3
REP MOVSB
ADD EDX, 4
DEC EAX
JNZ @@1
POP EBP
POP EBX
POP EDI
POP ESI
end;
end;
end;
 
我看比DreamTriger的应该快12倍以上。
1。DreamTrigger一次比较一个字节,
eYes一次判断4个字节
2.DreamTrigger用三条指令比较一次。
eYes用一条指令。
3.eYes的程序清晰易懂。
 

汇编!面强看个大概。
保存一下。
 
不会快12倍, 连1倍都没有. 性能如果能提高10-20%已经很了不起了.
 
呵呵,Another_eYes, 大家在捧你呢!
 
你试试长字符串(比如200M长)
 
你试试长字符串(比如200M长)
 
eyes:
佩服佩服,老实说,里面很多代码我都不懂,呵呵。我的汇编底子很薄的。
不过,最好在FindNext的时候要考虑一下中文,也就是说,如果发现SrcStr的
当前字符>=$80,就要移动两个字符进行下一轮比较。否则,中文匹配会出错。
就是下面这一段:(至于怎么改,我就不班门弄斧了)
@FindNext:
MOV ESI, SrcStr
INC ESI // 恢复参数准备查找下一轮匹配的字符
MOV ECX, EDX
MOV EDI, EBX
JMP @StartFind
 
呵呵. 看来DreamTiger看得还不够仔细. 没发觉FastPos中一个绝大的bug吗?
嘿嘿. 幸亏eYes手快, 已经消灭罪证喽
 
我又暈了:(
 
汇编不想看了,等几天,大家把BUG都找出来了再COPY,免得老大手再快一把……
 
CJ, 愿不愿帮忙测试一个函数库呀?(当然也是我写的)
主要过程和函数有:
.StrToDateTimeDef --> 字符串转DateTime, 对字符串格式没要求
(象: "兹定于: 2000年9月10日下午3点开会"这种字符串也能转成日期和时间)
.GetPartStr -> 取子串 (比如取出SQL语句中第三个参数名, 或者取出由第2组'[]'包起来的字符串)
.FastPos, FastPosNoCase, FastPosNext(查找字串第n次出现的位置),
FastPosLast FastReplace... --> 用处不用多说了.
.CreateGradient -> 在一幅BMP上填上渐进颜色效果(4种渐进方式: Horizon, Vertical, Rect, Ellipse, 可以设置开始点)
.XCombineBmp -> 对一幅BMP产生多种特效, 比如, 半透明, 渐进色, 背景充填另一幅BMP...多种效果可以同时出现(比如将另一BMP用渐进半透明方式画到当前BMP上)
.CreateRgnFromBmp -> 由一幅BMP生成它的边界REGION.
.DrawRectBorder -> 画3D边框, 有15种
.DrawRgnBorder -> 同上, 唯一区别是上面画的是个方框, 这里画的是不规则形状的3D边框
另外还有一些小函数, 这里就不写了.
有兴趣的话来封信, 我原代码发给你.
还包括我 开发的一组半透明带背景圆的TWinControl控件, 请帮忙一并测试一下
(这个任务比较艰巨, 没测试过, 只是编译通过). 包括:
ValueEdit,
PswdEdit(不怕偷看软件),
MagicEdit(输入的内容和显示在Edit上的可以完全是两码事, 比如可以直接用数字键输入中文大写)
HintEdit(鼠标移上去会显示超过Edit宽度被遮掉的字符)
ComboBox(IE地址栏那种功能)
LookupEdit(和ComboBox类似, 只是有焦点时才有下拉箭头, 当然还有另外一
些区别)
ListBox(除了ListBox本身外, 还集成了TreeView, CheckBox和Grid的部分功能)
Button(有Handle的, 很异类的那种)
ScrollBox(ScrollBar会象Windows的任务条一样自动隐藏)
 
我想的和CJ一样. :)
eyes: 函数库和控件能否给哦一份,我试一试. cakk@21cn.com
 
eyes:
既然在改了,为什么不连中文兼容性一起改了?
 
okok:
在 JMP @StartFind前加上:
OR AL, AL
JNS @StartFind
INC EDI
DEC ECX
 
嘿嘿,刚刚用上了 :)
 
eyes:
我也要qinghou@netease.com
 
老大,没消息啦?
 

Similar threads

I
回复
0
查看
698
import
I
I
回复
0
查看
756
import
I
I
回复
0
查看
688
import
I
I
回复
0
查看
601
import
I
I
回复
0
查看
594
import
I
顶部