delphi 中的win32 以外到平台的字符串处理一定慢吗?(转载)
原始连接:http://rvelthuis.blogspot.tw/2018/01/strings-on-other-platforms-than-32-bit.html
Strings too slow outside WIN32?
In a recent debate I had it was said that strings in the Win64 runtime are too slow to be useful. That is, in my opinion, a gross exaggeration. It is true that the Win32 runtime library (RTL) has benefited a lot from the work of the FastCode project, usually with routines in extremely clever assembler. For all other platforms, often the routines are in plain Object Pascal, so no assembler is being used. Also, far fewer routines have been replaced by clever implementations.
One very obvious example of this is the Pos
function, which searches if a certain string (I call that the Needle
) can be found in a larger one (the Haystack
). The Win32 implementation is in highly optimized assembler, written by Aleksandr Sharahov from the FastCode project, and licensed by CodeGear. The Win64 implementation is in plain Pascal (PUREPASCAL
). But the implementation for UnicodeString
is not the same, or even similar, to the implementation for AnsiString
!
The implementation for UnicodeString
is slower than the same routine for Win32. On my system a search in Win64 takes approx. 1.8 × the time it needs in Win32. On Win32, Pos
for AnsiString
is about as fast (or sometimes even slightly faster than) Pos
for UnicodeString
. But on Win64, Pos
for AnsiString
takes 2 × the time Pos
for UnicodeString
needs!
If you look at the sources in System.pas
, you'll see that the Unicode version is slightly better optimized (searching for the first Char
in the Needle
first, and only checking the rest if a match was found).
For fun, I took the code for the UnicodeString
implementation and converted it to work for AnsiString
. It was slightly faster than System.Pos
for UnicodeString
, instead of 2 times as slow. I wonder why, in System.pas
, the AnsiString
implementation does not simply use the same code as that for UnicodeString
, like I did. If I were a suspicious person, I would think it was done on purpose, to deprecate AnsiString
by making it less usable.
But even that can be improved upon. I wrote three implementations of my own routine, one for AnsiString
, one for UnicodeString
and one for TBytes
(many people have complained that TBytes
lacks something like Pos
and that was the reason they maintained the incredibly bad habit of using strings to store binary data — <shudder> — I wanted to take away that silly argument).
Code
Here is the code for my RVPosExA
function (for what it's worth: these days, there is no difference between PosEx
and Pos
anymore: both have the exact same functionality and signature):
function RVPosExA(const Needle, Haystack: AnsiString;
Offset: Integer = 1): Integer;
type
PUInt32 = ^UInt32;
PUInt16 = ^UInt16;
{$IFNDEF CPU32BITS}
var
LNeedleTip: UInt32;
PNeedle: PAnsiChar;
PHaystack, PEnd: PAnsiChar;
LLenNeedle: Integer;
LCmpMemOffset: Integer;
{$ENDIF}
begin
{$IFDEF CPU32BITS}
// FastCode (asm) implementation.
Result := System.Pos(Needle, Haystack, Offset);
{$ELSE}
if Offset - 1 + Length(Needle) > Length(Haystack) then
Exit(0);
Result := 0;
PHaystack := PAnsiChar(Haystack) + Offset - 1;
PEnd := PHaystack + Length(Haystack) - Length(Needle) + 1;
case Length(Needle) of
0: Exit(0);
1:
begin
LNeedleTip := PByte(Needle)^;
while PHaystack < PEnd do
if PByte(PHaystack)^ = LNeedleTip then
Exit(PHaystack - PAnsiChar(Haystack) + 1)
else
Inc(PHaystack);
Exit(0);
end;
2:
begin
LNeedleTip := PUInt16(Needle)^;
while PHaystack < PEnd do
if PUInt16(Haystack)^ = LNeedleTip then
Exit(PHayStack - PAnsiChar(Haystack) + 1)
else
Inc(PHaystack);
Exit(0);
end;
3:
begin
LNeedleTip := PUInt32(Needle)^; // if Needle is length 3, then top byte
// is the #0 terminator
while PHaystack < PEnd do
if ((PUInt32(Haystack)^ xor LNeedleTip) and $FFFFFF) = 0 then
Exit(PHaystack - PAnsiChar(Haystack) + 1)
else
Inc(PHaystack);
Exit(0);
end;
4:
begin
LNeedleTip := PUInt32(Needle)^;
while PHaystack < PEnd do
if PUInt32(Haystack)^ = LNeedleTip then
Exit(PHaystack - PAnsiChar(Haystack) + 1)
else
Inc(PHaystack);
Exit(0);
end;
else
begin
LCmpMemOffset := SizeOf(UInt32) div SizeOf(AnsiChar);
PNeedle := PAnsiChar(Needle) + LCmpMemOffset;
LLenNeedle := Length(Needle) - LCmpMemOffset;
LNeedleTip := PUInt32(Needle)^;
while PHaystack < PEnd do
if (PUInt32(PHaystack)^ = LNeedleTip) and
CompareMem(PHaystack + LCmpMemOffset, PNeedle, LLenNeedle) then
Exit(PHaystack - PAnsiChar(Haystack) + 1)
else
Inc(PHaystack);
end;
end;
{$ENDIF}
end;
As you can see, under Win32, it simply jumps to System.Pos
, as that is the fastest anyway. But on all other platforms, it searches the Haystack
4-byte-wise (if the Needle
is larger than 4 elements), and if it found something, then it searches the rest using CompareMem
.
Timing
Here is a slightly reformatted output of a test program (I put the WIN32 and the WIN64 columns beside each other, to save space):
Different versions of Pos(Needle, Haystack: <sometype>; Offset: Integer): Integer
where <sometype> is UnicodeString, AnsiString or TBytes Testing with Haystack lengths of 50, 200, 3000, 4000 and 300000
and Needle lengths of 1, 3, 8 and 20
5 * 4 * 2000 = 40000 loops WIN64 WIN32 UnicodeString UnicodeString
------------- -------------
System.Pos: 2428 ms System.Pos: 1051 ms
StrUtils.PosEx: 2258 ms StrUtils.PosEx: 1070 ms
RVPosExU: 1071 ms RVPosExU: 1050 ms AnsiString AnsiString
---------- ----------
System.Pos: 4956 ms System.Pos: 1046 ms
AnsiStrings.PosEx: 4959 ms AnsiStrings.PosEx: 1051 ms
OrgPosA: 5129 ms OrgPosA: 5712 ms
PosUModForA: 1958 ms PosUModForA: 3744 ms
RVPosExA: 1322 ms RVPosExA: 1086 ms TBytes TBytes
------ ------
RVPosEXB: 998 ms RVPosEXB: 2754 ms Haystack: random string of 500000000 ASCII characters or bytes
Needle: last 10 characters of Haystack = 'WRDURJVDFA' WIN64 WIN32 UnicodeString UnicodeString
------------- -------------
System.Pos: 847 ms System.Pos: 421 ms
Strutils.PosEx: 827 ms Strutils.PosEx: 414 ms
RVPosExU: 421 ms RVPosExU: 438 ms AnsiString AnsiString
---------- ----------
System.Pos: 1735 ms System.Pos: 428 ms
AnsiStrings.PosEx: 1831 ms AnsiStrings.PosEx: 428 ms
OrgPosA: 1749 ms OrgPosA: 2687 ms
PosUModForA: 708 ms PosUModForA: 1525 ms
RVPosExA: 368 ms RVPosExA: 423 ms
RvPosExA(,,Offset): 200 ms RvPosExA(,,Offset): 220 ms TBytes TBytes
------ ------
RVPosExB(TBytes): 385 ms RVPosExB(TBytes): 1095 ms
The routines RVPosExA
, RVPosExU
and RVPosExB
are my implementations for AnsiString
, UnicodeString
and TBytes
respectively. OrgPosA
is the original code for Pos
for AnsiString
, while PosUModForA
is the original PUREPASCAL code for Pos
for UnicodeString
, modified for AnsiString
.
As you can see, the PosUModForA
routine is almost twice as fast as the rather braindead OrgPosA
, and in WIN32, the RVPosEx<A/U/B>
implementations are faster than the others.
I didn't check, but it is well possible that one of the plain Pascal versions of the FastCode project is faster. But for me, this implementation is a start and proof, that with a few simple optimizations string routines could be made faster. Perhaps, one day, Embarcadero will adopt more of the plain Pascal code from the FastCode project.
The code for the routines and the program that produces the output above can be downloaded from my website.
delphi 中的win32 以外到平台的字符串处理一定慢吗?(转载)的更多相关文章
- BCB/Delphi中常用的VCL函数说明(字符串函数)
本文档是ccrun(老妖)根据网上资料整理而成. --------------------内存分配--------------------函数名称:AllocMem函数说明:在队中分配指定字节的内存块 ...
- Delphi中的关键字与保留字
Delphi中的关键字与保留字 分类整理 Delphi 中的“关键字”和“保留字”,方便查询 感谢原作者的收集整理! 关键字和保留字的区别在于,关键字不推荐作标示符(编译器已经内置相关函数或者留给保留 ...
- Delphi中SendMessage使用说明(所有消息说明) good
Delphi中SendMessage使用说明 SendMessage基础知识 函数功能:该函数将指定的消息发送到一个或多个窗口.此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回.而函数Po ...
- delphi中SendMessage使用说明
SendMessage基础知识 函数功能:该函数将指定的消息发送到一个或多个窗口.此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回.而函数PostMessage不同,将一个消息寄送到一个线 ...
- Delphi中编辑word
其他(28) //启动Word try wordapplication1.connect; except messagedlg('word may not be ins ...
- Delphi中代替WebBrowser控件的第三方控件
这几天,接触到在delphi中内嵌网页,用delphi7自带的TWebBrowser控件,显示的内容与本机IE8显示的不一样,但是跟装IE8之前的IE6显示一个效果.现在赶脚是下面两个原因中的一个: ...
- [转]Delphi中,让程序只运行一次的方法
program onlyRunOne; uses Forms,Windows,SysUtils, Dialogs, Unit1 in 'Unit1.pas' {Form1}; {$R *.res} v ...
- 在C#中使用 Win32 和其他库
C# 用户经常提出两个问题:“我为什么要另外编写代码来使用内置于 Windows® 中的功能?在框架中为什么没有相应的内容可以为我完成这一任务?”当框架小组构建他们的 .NET 部分时,他们评估了为使 ...
- DELPHI语法基础学习笔记-Windows 句柄、回调函数、函数重载等(Delphi中很少需要直接使用句柄,因为句柄藏在窗体、 位图及其他Delphi 对象的内部)
函数重载重载的思想很简单:编译器允许你用同一名字定义多个函数或过程,只要它们所带的参数不同.实际上,编译器是通过检测参数来确定需要调用的例程.下面是从VCL 的数学单元(Math Unit)中摘录的一 ...
随机推荐
- centos 7.2 安装域名服务器(bind9.9 集群--主从架构),私有域名服务器+缓存
1.安装组件 yum install bind bind-utils -y 2.启动域名服务 service named start chkconfig named on ss -unlt |grep ...
- 顺时针打印矩阵(python)
题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数 ...
- python爬虫相关基础概念
什么是爬虫 爬虫就是通过编写程序模拟浏览器上网,然后让其去互联网上抓取数据的过程. 哪些语言可以实现爬虫 1.php:可以实现爬虫.但是php在实现爬虫中支持多线程和多进程方面做得不好. 2.java ...
- 利用crontab每天定时备份MySQL数据库
当数据库服务器建立并正式投入生产使用后,我们不得不担忧一个问题:当数据库遭到破坏后,怎样安然恢复到最后一次正常的状态,使得数据的损失达到最小. 我这里以本博客的wordpress数据为例,来讨论并实现 ...
- vue 给v-html中的元素设置样式
解决方案:写样式的时候添加>>>
- 865. Smallest Subtree with all the Deepest Nodes 有最深节点的最小子树
[抄题]: Given a binary tree rooted at root, the depth of each node is the shortest distance to the roo ...
- Day 04 列表,元祖,range
列表: why: 1.字符串取值费劲 2.对字符串做任何操作,取出来的都是字符串 3.字符串有长度限制 基于以上原因,python提供了另一个数据类型,list 容器类数据类型. 列表页脚数组,可以存 ...
- mysql decimal(10,2)对应java类型
下面我给出MYSQL类型与JAVA类型对应表,希望能够帮到您: 类型名称 显示长度 数据库类型 JAVA类型 JDBC类型索引(int) VARCHAR L+N VARCHAR java.lang.S ...
- if 循环的深入理解 哈希表的一种应用
哈希表的值作为一个颜色容器,值默认为标识1, 表示未曾用过,若用过标识为0: 1: 程序第一步 遍历哈希表,查找标识为1 未曾用过的颜色 我用了这个: string colorno_us ...
- Myeclipse2013破解方法
1.先关闭Myeclipse2013 2.(1)输入usercode可以随便输入,(2)然后选择Myeclipse的版本,(3)点击systemid按钮 3.点击Tools菜单栏下的RebuildKe ...