在Delphi的早期版本中,并没有提供UnicodeString,而WideString缓慢的性能使许多使用宽字符串的应用性能不高。本文提供了一种方法,来优化早期的宽字符串速度。
【注意】本文并没有完整实现所有需要的方法,如果您有兴趣,自己实现剩下的部分吧。
我们知道WideString速度慢的一个重要原因是没有引用计数,因此,需要字符串的频率拷贝处理,造成系统性能低下,我们就从此入口,提升性能。
同时,由于WideString是内置类型,其分配和释放系统自动来决定,我们的实现也应达到这个要求。我们知道interface会自动管理计数器,而结构中的interface,Delphi也会自动管理生命周期,所以,下面的示例代码就是用了这样一个技巧,我们来看声明:
IQStringW=interface procedure From(S:WideString);overload; procedure From(p:PWideChar;len:NativeInt);overload; function Copy:IQStringW; function Data:PWideChar; function Length:Integer; end; TQStringW=class(TInterfacedObject,IQStringW) private FData:array of WideChar; public procedure From(S:WideString);overload; procedure From(p:PWideChar;len:NativeInt);overload; function Copy:IQStringW; function Data:PWideChar; function Length:Integer; end; QStringW=record private Data:IQStringW; public class operator Implicit(const S:WideString):QStringW; class operator Implicit(const p:PWideChar):QStringW; class operator Implicit(const S:QStringW):PWideChar; class operator Implicit(const S:QStringW):WideString; function Copy:QStringW; end;
我们的QStringW是一个记录,内部唯一的数据成员Data是一个IQStringW接口,这样赋值时,记录将自动管理Data的引用计数,至于下面的一些重载,仅仅是演示,实际应用还需要重载更多的操作符,以方便操作。下面是实现的代码:
{ QStringW } class operator QStringW.Implicit(const p: PWideChar): QStringW; begin Result.Data:=TQStringW.Create; Result.Data.From(String(p)); end; class operator QStringW.Implicit(const S: WideString): QStringW; begin Result.Data:=TQStringW.Create; Result.Data.From(S); end; class operator QStringW.Implicit(const S: QStringW): WideString; begin if Assigned(S.Data) then Result:=System.Copy(S.Data.Data,0,S.Data.Length) else SetLength(Result,0); end; class operator QStringW.Implicit(const S: QStringW): PWideChar; begin if Assigned(S.Data) then Result:=S.Data.Data else Result:=nil; end; function QStringW.Copy: QStringW; begin if Assigned(Data) then Result.Data:=Data.Copy else Result.Data:=nil; end; { TQStringW } function TQStringW.Copy: IQStringW; var AResult:TQStringW; L:NativeInt; begin AResult:=TQStringW.Create; L:=System.Length(FData); SetLength(AResult.FData,L); if L>0 then Move(FData[0],AResult.FData[0],L shl 1); Result:=AResult; end; function TQStringW.Data: PWideChar; begin Result:=PWideChar(@FData[0]); end; procedure TQStringW.From(S: WideString); var L:NativeInt; begin L:=System.Length(S); SetLength(FData,L+1); if L>0 then Move(PWideChar(S)^,FData[0],L shl 1); FData[L]:=#0; end; procedure TQStringW.From(p: PWideChar; len: NativeInt); begin if len<0 then From(String(p)) else begin SetLength(FData,len+1); if len>0 then Move(p^,FData[0],len shl 1); FData[len]:=#0; end; end; function TQStringW.Length: Integer; begin Result:=System.Length(FData); end;
由于我们只实现了赋值操作值,那么我们对比下赋值的性能:
var S,S1:QStringW; WS,WS1:WideString; U,US1:UnicodeString; I: Integer; T1,T2,T3:DWord; begin S:='Hello,world'; WS:='Hello,world'; U:='Hello,world'; T1:=GetTickCount; for I := 0 to 1000000 do S1:=S; T1:=GetTickCount-T1; T2:=GetTickCount; for I := 0 to 1000000 do WS1:=WS; T2:=GetTickCount-T2; T3:=GetTickCount; for I := 0 to 1000000 do US1:=U; T3:=GetTickCount-T3; ShowMessage('QStringW='+IntToStr(T1)+'ms,WideString='+IntToStr(T2)+'ms,UnicodeString='+IntToStr(T3)+'ms'); end;
为了对比Delphi2009后的UnicodeString性能,上面的测试代码在XE6 Update1上执行,并加入UnicodeString对比,结果如下: