procedure TForm1.TestSO; var jo: ISuperObject; i: Int64; sw: TStopWatch; begin sw := TStopWatch.Create; jo := SO(); i := 0; while i < cstMaxTest do begin jo.S['Name'] := 'This is a Str' + IntToStr(i); jo.I['Age'] := i; jo.O['List'] := SO('[1,"Hello",5,{"name":"c5soft","age":50}]'); if i = 100 then Log('SO:' + jo.AsJSon()); inc(i); end; Log('SuperObject: ' + sw.Stop()); sw.free; end;
procedure TForm1.TestMJ; var jo: Variant; i: Int64; sw: TStopWatch; begin TDocVariant.New(jo); ; sw := TStopWatch.Create; i := 0; while i < cstMaxTest do begin jo.Name := 'This is a Str' + IntToStr(i); jo.Age := i; jo.List:=_JSon('[1,"Hello",5,{"name":"c5soft","age":50}]'); if i = 100 then Log('MJ:' + VariantSaveJSON(jo)); inc(i); end; Log('mORMot JS: ' + sw.Stop()); sw.free; end;
做一个10万次的测试,这是输出结果: SO:{"Age":100,"Name":"This is a Str100","List":[1,"Hello",5,{"age":50,"name":"c5soft"}]} SuperObject: 00:01.561 MJ:{"Name":"This is a Str100","Age":100,"List":[1,"Hello",5,{"name":"c5soft","age":50}]} mORMot JS: 00:00.326
发现了吧,mORMot比SuperObject快太多,干同样的活,SuperObject用的时间是mORMot的5倍。 使用mORMot还用另外两个好处: 其一、用jo.Name代替jo.S['Name']更直观 其二、在调试阶段设置断点,观察jo的值,delphi告诉你:{"Name":"This is a Str100","Age":100,"List":[1,"Hello",5,{"name":"c5soft","age":50} 而你去跟踪SuperObject的jo,Delphi只能告诉你那是一个对象,具体值是多少,她说“太复杂,一言能尽”,结果什么也看不到。
随便做一下JsonDataObject的测试,因为JsonDataObject是基于UnicodeString的,改用Berlin编译,运行结果如下: SO:{"Age":100,"Name":"This is a Str100","List":[1,"Hello",5,{"age":50,"name":"c5soft"}]} SuperObject: 00:00.880 JD:{"Name":"This is a Str100","Age":100,"List":[1,"Hello",5,{"name":"c5soft","age":50}]} JsonDataObject: 00:00.084 MJ:{"Name":"This is a Str100","Age":100,"List":[1,"Hello",5,{"name":"c5soft","age":50}]} mORMot JS: 00:00.170 这回JsonObject胜出,比mORMot节省一半的时间。这个结果与前面的是同一台电脑上跑出来的结果,前面的是Delphi7编译的,可见berlin编译出来的代码运行速度比delphi7快了差不多1倍。
----------------------------------------------
-
JsonDataObject的代码这样写: procedure TForm1.TestJD; var jo: TJSonObject; i: Int64; sw: TStopWatch; begin sw := TStopWatch.Create; jo := TJSonObject.Create; i := 0; while i < cstMaxTest do begin jo.S['Name'] := 'This is a Str' + IntToStr(i); jo.I['Age'] := i; jo.A['List'] :=TJSonArray(TJSonObject.Parse('[1,"Hello",5,{"name":"c5soft","age":50}]')); if i = 100 then Log('JD:' + jo.ToJSon()); inc(i); end; Log('JsonDataObject: ' + sw.Stop()); jo.Free; sw.free; end;
----------------------------------------------
-
上面是32Bit的测试结果,再跑一个64bit的: SO:{"Age":100,"Name":"This is a Str100","List":[1,"Hello",5,{"age":50,"name":"c5soft"}]} SuperObject: 00:01.291 JD:{"Name":"This is a Str100","Age":100,"List":[1,"Hello",5,{"name":"c5soft","age":50}]} JsonDataObject: 00:00.104 MJ:{"Name":"This is a Str100","Age":100,"List":[1,"Hello",5,{"name":"c5soft","age":50}]} mORMot JS: 00:00.169
SO:{"Age":100,"Name":"This is a Str100","List":[1,"Hello",5,{"age":50,"name":"c5soft"}]} SuperObject: 00:01.288 JD:{"Name":"This is a Str100","Age":100,"List":[1,"Hello",5,{"name":"c5soft","age":50}]} JsonDataObject: 00:00.116 MJ:{"Name":"This is a Str100","Age":100,"List":[1,"Hello",5,{"name":"c5soft","age":50}]} mORMot JS: 00:00.169
D:\Dev\Lib\SQLite3\Samples\09 - HttpApi web server>ab -n50000 -c10000 http://localhost/hello This is ApacheBench, Version 2.3 <$Revision: 655654 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/
Server Software: Synopse Server Hostname: localhost Server Port: 80
Document Path: /hello Document Length: 38 bytes
Concurrency Level: 10000 Time taken for tests: 21.768 seconds Complete requests: 50000 Failed requests: 0 Write errors: 0 Total transferred: 15750000 bytes HTML transferred: 1900000 bytes Requests per second: 2297.00 [#/sec] (mean) Time per request: 4353.503 [ms] (mean) Time per request: 0.435 [ms] (mean, across all concurrent requests) Transfer rate: 706.60 [Kbytes/sec] received
Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.3 0 15 Processing: 1569 4086 751.4 4496 4627 Waiting: 404 2131 1067.8 2018 4205 Total: 1569 4086 751.4 4496 4627
Percentage of the requests served within a certain time (ms) 50% 4496 66% 4582 75% 4594 80% 4596 90% 4606 95% 4615 98% 4621 99% 4623 100% 4627 (longest request)
D:\Dev\Lib\SQLite3\Samples\09 - HttpApi web server>pause 请按任意键继续. . .
Concurrency Level(并发数): 10000 Time taken for tests(测试耗费时间): 51.937 seconds Complete requests(总的请求数): 100000 Failed requests(失败请求数): 0 Write errors: 0 Total transferred: 1405200000 bytes HTML transferred: 1381200000 bytes Requests per second(每秒请求数(平均)): 1925.42 [#/sec] (mean) Time per request(每次并发请求时间(所有并发)): 5193.685 [ms] (mean) Time per request(每一请求时间(单个并发)): 0.519 [ms] (mean, across all concurrent requests) Transfer rate(传输速率): 26421.81 [Kbytes/sec] received
Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.4 0 8 Processing: 1648 5032 709.1 5241 5889 Waiting: 944 2487 536.5 2439 3985 Total: 1648 5032 709.1 5241 5889
Percentage of the requests served within a certain time (ms) 50% 5241 66% 5279 75% 5298 80% 5317 90% 5420 95% 5675 98% 5798 99% 5830 100% 5889 (longest request)
----------------------------------------------
-
官方文档 8.3.7 By default, all ORM read operations will be run in concurrent mode, and all ORM write operations will be executed in blocking mode. This is expected to be both safe and fast, with our internal SQLite3 engine, or most of the external databases.
如果要输出很大的数据到终端界面,一定要使用分页。即使Delphi生成json数据 不慢,网络传送速度快不了,前端(比如浏览器)装配数据快不了。SQLite的select 支持分页: SELECT ... FROM ... WHERE ... ORDER BY .. LIMIT... OFFSET...
procedure TForm1.TestMJ; var jo: Variant; i: Int64; sw: TStopWatch; begin TDocVariant.New(jo); ; sw := TStopWatch.Create; i := 0; while i < cstMaxTest do begin jo.Name := 'This is a Str' + IntToStr(i); jo.Age := i; jo.List:=_JSon('[1,"Hello",5,{"name":"c5soft","age":50}]'); if i = 100 then Log('MJ:' + VariantSaveJSON(jo)); inc(i); end; Log('mORMot JS: ' + sw.Stop()); sw.free; end;
哪位仁兄有 mORMot Enhanced System Run Time for Delphi 7 and Delphi 2007 源代码? https://synopse.info/forum/viewtopic.php?id=23
----------------------------------------------
-
Original Source code remains on following copyright: Copyright (c) 1988, 2001 Borland Software Corporation
A.Bouchez disclaims copyright to the modifications made to the original code. In place of a legal notice, here is a blessing (from SQLite3 licence): May you do good and not evil. May you find forgiveness for yourself and forgive others. May you share freely, never taking more than you give.
2. Installation
These files are hacked low-level source code, extracted from the original Delphi 7 and 2007 run time libray and VCL.
In order to install them, you MUST have a true Delphi 7/2007 installation in your PC, then launch the SynopseRTL.EXE file. This file will check for the Delphi source files and ask for a destination folder. Keep the destination Directory name short (aka D:\Dev\Lib) for easy maintenability. Then launch syscomp.bat to compile the enhanced special System.dcu and SysInit.dcu files. The original source files are needed: this installation package only contains the diffs made to these files.
In order to use the enhanced files, you must change your Delphi settings, and place the new files to be look up by the Delph compiler BEFORE the default ones (in C:\Program Files\Borland\Delphi7): - for the IDE, make sure that the new directory (e.g. D:\Dev\Lib) is the FIRST in all paths ("Library" tab of the global Options), before $(DELPHI)\Lib: a valid parameter is "d:\dev\lib;$(DELPHI)\Lib;$(DELPHI)\Bin;...." or "d:\Dev\Lib;$(DELPHI)\source\vcl;$(DELPHI)\Source\Rtl;...." - for the command line compiler, change the dcc32.cfg -u flag content in the C:\Program Files\Borland\Delphi7\Bin directory into this: -ud:\dev\lib;"C:\Program Files\Borland\Delphi7\lib" or use a custom bat file file the proper -ud:\dev\lib command line to dcc32.exe
These files are needed for the Synopse SQLite3 framework to compile. If you don't want to hack your compiler, copy System.dcu and SysInit.dcu files into your Synopse SQLite3 framework program directory.
I managed to get this RTL work with Delphi 2007. Only System.pas and SysInit.pas are supplied (and needed) for Delphi 2007. Even if some John O'Harrow or Pierre Le Riche modifications are already included in Delphi 2007, some of my tricks are unique and could be usefull (see below). Only Delphi 7 and Delphi 2007 patches exists till now, since I need the original source files, in order to create the patches (by design). :)
Enjoy!
3. System.pas enhancements (Delphi 7 and Delphi 2007)
- included John O'Harrow version of move() fillchar() Pos() Val() and Int64 math - included Pierre Le Riche fast _LStrCmp() implementation - deprecated Japan Nec computer bug prevention under Win95 is removed - shortstring delete() copy() now use move() and not slow "rep movsb/movsd" - borland memory manager is removed and replaced with windows heap manager: we recommend using FastMM4 explicitely for VCL applications (as Delphi 2009), and leave as it is for smaller executables (e.g. for LVCL; the windows heap manager is slower than FastMM4, but sufficient for GUI small apps) - there is an optional FastMM4 memory manager inside our getmem.inc, which can be enabled by undefining HEAPMEMMANAGER conditional in getmem.inc: this FastMM4 shares its memory for the same process with external .DLL (if both EXE and DLL are compiled with undefined HEAPMEMMANAGER conditional) - some common functions implemented in asm (_Getmem, _Freemem, IOResult...) - very common internal basic methods rewritten or improved: TObject.NewInstance/InitInstance/InheritsFrom/_AfterConstruction/ GetDynaMethod/ClassName... - on new multi-core CPU, avoid lock asm usage for strings and dynamic arrays if no TThread is used (default $define AVOIDLOCK): faster with high-end PC - record and object initialization and finalization rewritten for speed: _InitializeArray, _FinalizeArray, _InitializeRecord, _FinalizeRecord - resourcestring are cached (default $define LOADRESSTRINGCACHED) - resourcestring can be translated on the fly with the global LoadResStringTranslate procedure pointer (this feature is used in the SQLite3i18n unit) - widestring allocation can be cached (if $define CACHE_WIDESTRINGS is set) (with Vista, the caching is always disabled, since this OS is much faster than the previous) but you must call explicitely InitWideStringOptimize to launch caching, in order to use the right heap manager (if you intend to use FastMM4 e.g.) - asm code: 'rep ret' optimization avoid branch misprediction for AMD64 CPUs http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25112.PDF see '6.2 Two-Byte Near-Return RET Instruction', page 128 - asm code XOR reg32,reg32 + MOV reg8,* is changed to faster MOVZX reg32,* - a special version is compiled with the LVCL define flag, for even smaller code - Linux/Kylix compatibility: . can be compiled with the command-line compiler (tested with crosskylix) . codepage is fixed to ISO-8859-1: widestring is available with configuration like LANG=*.UTF-8, which is the standard nowadays for modern distros
4. SysUtils.pas enhancements (Delphi 7 and Delphi 2007)
- NormToLower[] and NormToUpper[] tables to fast character case conversion - TwoDigitLookup[] table for fast 0..99 to '00'..'99' conversion - HexChars[] for fast 0..15 to '0'..'f' conversion - Ansi*() functions use NormToLower[] and NormToUpper[]: call i18n unit and i18n*() functions for using current locale - some common functions are faster than default Borland's implementation: CompareText() SameText() UpperCase() IntToStr() DecodeTime() DecodeDate() StrLen() StrComp() StrIComp() StrEnd() and avoid use of slow PIC specific code or lodsb/stosb asm opcodes in asm
5. Classes.pas enhancements (Delphi 7)
- faster TReader.Read() and TCustomMemoryStream.Seek() - faster TList.Get(), TCollection.GetItem() and TStringList.Get() - under Linux, avoid variants usage for speed up and code size
- fix the VCL TDateTimePicker checkbox handling bug for Windows Vista
8. Grids.pas enhancements (Delphi 7)
- fix issue: Grid flickers with active theming - use XP Themes for fixed cells if enabled
9. Printer.pas enhancements (Delphi 7)
- allows raw customization of low level parameters: . create a TPrinterNew instance instead of a TPrinter . access to color/black&white or duplex mode e.g. with PrinterNew function . we have to subclass TPrinter inside this unit, because Borland made the key properties and methods of the class private :(
10. IniFiles.pas enhancements (Delphi 7)
- faster hash implementation (in asm) - exact same hashing algorythm and value - round Hash buffer Size to a power of two (faster hashing calculation)
11. TypInfo.pas enhancements (Delphi 7)
- under Linux, avoid variants usage for speed up and code size
procedure TSynBinaryDataSet.InternalInitFieldDefs; var F: integer; DBType: TFieldType; begin FieldDefs.Clear; if fDataAccess=nil then exit; for F := 0 to fDataAccess.ColumnCount-1 do with fDataAccess.Columns[F] do begin case ColumnType of SynCommons.ftInt64: DBType := ftLargeint; SynCommons.ftDate: DBType := ftDateTime; SynCommons.ftUTF8: if ColumnDataSize=0 then DBType := ftWideString else //ftDefaultMemo else DBType := ftWideString; // means UnicodeString for Delphi 2009+ SynCommons.ftBlob: DBType := ftBlob; SynCommons.ftDouble, SynCommons.ftCurrency: DBType := ftFloat; else raise EDatabaseError.CreateFmt( 'GetFieldData ColumnType=%s',[TSQLDBFieldTypeToString(ColumnType)]); end; FieldDefs.Add(UTF8ToString(ColumnName),DBType,ColumnDataSize); end; end;
----------------------------------------------
-
procedure TForm1.TestSo; var jo: ISuperObject; i: Int64; sw: TStopWatch; begin sw := TStopWatch.Create; jo := SO(); i := 0; while i < testVarLoop do begin jo.S['Name'] := 'This is a Str' + IntToStr(i); jo.I['Age'] := i; if testList.count > 0 then jo.O['List'] := SO(testList.text) else jo.O['List'] := SO('[1,"Hello",5,{"name":"c5soft","age":50}]'); if i = 100 then memo1.lines.add('SO:' + jo.AsJSon()); inc(i); end; memo1.lines.add('SuperObject: ' + sw.Stop()); sw.free; end;
procedure TForm1.TestJD; var jo: TJSonObject; i: Int64; sw: TStopWatch; begin sw := TStopWatch.Create; jo := TJSonObject.Create; i := 0; while i < testVarLoop do begin jo.S['Name'] := 'This is a Str' + IntToStr(i); jo.I['Age'] := i; if testList.count > 0 then jo.O['List'] := (TJSonObject.Parseutf8(testList.text) as tjsonobject) else jo.A['List'] := TJSonArray(TJSonObject.Parse('[1,"Hello",5,{"name":"c5soft","age":50}]')); if i = 100 then memo1.lines.add('JD:' + jo.ToJSon()); inc(i); end; memo1.lines.add('JsonDataObject: ' + sw.Stop()); jo.Free; sw.free; end;
procedure TForm1.TestMJ; var jo: Variant; i: Int64; sw: TStopWatch; begin TDocVariant.New(jo); sw := TStopWatch.Create; i := 0; while i < testVarLoop do begin jo.Name := 'This is a Str' + IntToStr(i); jo.Age := i; if testList.count > 0 then jo.List := _JSon(testList.text) else jo.List := _JSon('[1,"Hello",5,{"name":"c5soft","age":50}]'); if i = 100 then memo1.lines.add('MJ:' + VariantSaveJSON(jo)); inc(i); end; memo1.lines.add('mORMot JS: ' + sw.Stop()); sw.free; end;