You are not logged in.
Pages: 1
Hello,
Interesting.
I added another generic collections lib which on
https://github.com/d-mozulyov/Rapid.Generics
program SpringVsMormotCollections;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
System.Diagnostics,
mormot.core.collections,
Spring,
Spring.Collections
, Rapid.Generics
;
{ ----------------------------------------------------------------------- }
type
TBenchmarks = class
private
function CreateTestListString(ACount: Integer; const APrefix: String): TArray<String>;
procedure RunBenchmark(const AProc: TProc; ABenchmarkRounds: Integer);
procedure ListBenchmark;
procedure DictBenchmark;
public
procedure Run;
end;
{ ======================================================================= }
{ TBenchmarks }
{ ======================================================================= }
function TBenchmarks.CreateTestListString(ACount: Integer; const APrefix: String): TArray<String>;
begin
SetLength(Result, ACount);
for var Loop:=0 to ACount-1
do Result[Loop]:=APrefix+' #'+IntToStr(Loop+1);
end;
{ ----------------------------------------------------------------------- }
procedure TBenchmarks.DictBenchmark;
const
BenchRounds = 100;
begin
var TestKeys:=CreateTestListString(100000,'Key');
var TestValues:=CreateTestListString(100000,'Value');
var MormotDict:=Collections.NewKeyValue<String,String>;
var SpringDict:=TCollections.CreateDictionary<String,String>;
var RapidDict:=Rapid.Generics.TRapidDictionary<String, String>.Create;
Writeln(Format('Populate MormotDict with %d items...',[Length(TestKeys)]));
RunBenchmark(
procedure
begin
MormotDict.Clear;
for var Loop:=0 to Length(TestKeys)-1
do MormotDict.Add(TestKeys[Loop],TestValues[Loop]);
end,BenchRounds);
Writeln(Format('Populate SpringDict with %d items...',[Length(TestKeys)]));
RunBenchmark(
procedure
begin
SpringDict.Clear;
for var Loop:=0 to Length(TestKeys)-1
do SpringDict.Add(TestKeys[Loop],TestValues[Loop]);
end,BenchRounds);
Writeln(Format('Populate RapidDict with %d items...',[Length(TestKeys)]));
RunBenchmark(
procedure
begin
RapidDict.Clear;
for var Loop:=0 to Length(TestKeys)-1
do RapidDict.Add(TestKeys[Loop],TestValues[Loop]);
end,BenchRounds);
Writeln(Format('---------------------------------... %d',[Length(TestKeys)]));
Writeln('Compare MormotDict with SpringDict...');
RunBenchmark(
procedure
var
CurKey : String;
CurMormotValue: String;
CurSpringValue: String;
begin
for var Loop:=0 to Length(TestKeys)-1 do
begin
CurKey:=TestKeys[Loop];
if not
(
MormotDict.TryGetValue(CurKey,CurMormotValue) and
SpringDict.TryGetValue(CurKey,CurSpringValue) and
(CurMormotValue=CurSpringValue)
)
then raise Exception.Create('Different content!');
end;
end,BenchRounds);
Writeln('Compare MormotDict with RapidDict...');
RunBenchmark(
procedure
var
CurKey : String;
CurMormotValue: String;
CurSpringValue: String;
begin
for var Loop:=0 to Length(TestKeys)-1 do
begin
CurKey:=TestKeys[Loop];
if not
(
MormotDict.TryGetValue(CurKey,CurMormotValue) and
RapidDict.TryGetValue(CurKey,CurSpringValue) and
(CurMormotValue=CurSpringValue)
)
then raise Exception.Create('Different content!');
end;
end,BenchRounds);
Writeln('Compare SpringDict with RapidDict...');
RunBenchmark(
procedure
var
CurKey : String;
CurMormotValue: String;
CurSpringValue: String;
begin
for var Loop:=0 to Length(TestKeys)-1 do
begin
CurKey:=TestKeys[Loop];
if not
(
RapidDict.TryGetValue(CurKey,CurSpringValue) and
SpringDict.TryGetValue(CurKey,CurMormotValue) and
(CurMormotValue=CurSpringValue)
)
then raise Exception.Create('Different content!');
end;
end,BenchRounds);
Writeln(Format('---------------------------------... %d',[Length(TestKeys)]));
Writeln('Iterate MormotDict by key (TryGetValue)...');
RunBenchmark(
procedure
var
CurKey : String;
CurValue: String;
begin
for var Loop:=0 to Length(TestKeys)-1 do
begin
CurKey:=TestKeys[Loop];
if not MormotDict.TryGetValue(CurKey,CurValue)
then raise Exception.Create('Something is wrong!');
end;
end,BenchRounds);
Writeln('Iterate SpringDict by key (TryGetValue)...');
RunBenchmark(
procedure
var
CurKey : String;
CurValue: String;
begin
for var Loop:=0 to Length(TestKeys)-1 do
begin
CurKey:=TestKeys[Loop];
if not SpringDict.TryGetValue(CurKey,CurValue)
then raise Exception.Create('Something is wrong!');
end;
end,BenchRounds);
Writeln('Iterate RapidDict by key (TryGetValue)...');
RunBenchmark(
procedure
var
CurKey : String;
CurValue: String;
begin
for var Loop:=0 to Length(TestKeys)-1 do
begin
CurKey:=TestKeys[Loop];
if not RapidDict.TryGetValue(CurKey,CurValue)
then raise Exception.Create('Something is wrong!');
end;
end,BenchRounds);
Writeln(Format('---------------------------------... %d',[Length(TestKeys)]));
Writeln('Iterate (for..in) MormotDict ...');
RunBenchmark(
procedure
var
CurKey : String;
CurValue: String;
begin
for var Entry in MormotDict do
begin
CurKey:=Entry.Key;
CurValue:=Entry.Value;
end;
end,BenchRounds);
Writeln('Iterate (for..in) SpringDict ...');
RunBenchmark(
procedure
var
CurKey : String;
CurValue: String;
begin
for var Entry in SpringDict do
begin
CurKey:=Entry.Key;
CurValue:=Entry.Value;
end;
end,BenchRounds);
Writeln('Iterate (for..in) RapidDict ...');
RunBenchmark(
procedure
var
CurKey : String;
CurValue: String;
begin
for var Entry in RapidDict do
begin
CurKey:=Entry.Key;
CurValue:=Entry.Value;
end;
end,BenchRounds);
Writeln(Format('---------------------------------... %d',[Length(TestKeys)]));
end;
{ ----------------------------------------------------------------------- }
procedure TBenchmarks.ListBenchmark;
const
BenchRounds = 100;
begin
var TestData:=CreateTestListString(100000,'Value');
var MormotList:=Collections.NewList<String>;
var SpringList:=TCollections.CreateList<String>;
var RapidList:=Rapid.Generics.TList<string>.create;
Writeln(Format('Populate MormotList with %d items...',[Length(TestData)]));
RunBenchmark(
procedure
begin
MormotList.Clear;
for var Entry in TestData do
begin
MormotList.Add(Entry);
end;
end,BenchRounds);
Writeln(Format('Populate SpringList with %d items...',[Length(TestData)]));
RunBenchmark(
procedure
begin
SpringList.Clear;
for var Entry in TestData do
begin
SpringList.Add(Entry);
end;
end,BenchRounds);
Writeln(Format('Populate RapidList with %d items...',[Length(TestData)]));
RunBenchmark(
procedure
begin
RapidList.Clear;
for var Entry in TestData do
begin
RapidList.Add(Entry);
end;
end,BenchRounds);
Writeln(Format('---------------------------------... %d',[Length(TestData)]));
Writeln('Compare MormotList with SpringList...');
RunBenchmark(
procedure
begin
for var Loop:=0 to SpringList.Count-1 do
begin
if MormotList[Loop]<>SpringList[Loop]
then raise Exception.Create('Different content!');
end;
end,BenchRounds);
Writeln('Compare MormotList with RapidList...');
RunBenchmark(
procedure
begin
for var Loop:=0 to MormotList.Count-1 do
begin
if MormotList[Loop]<>RapidList[Loop]
then raise Exception.Create('Different content!');
end;
end,BenchRounds);
Writeln('Compare SpringList with RapidList...');
RunBenchmark(
procedure
begin
for var Loop:=0 to SpringList.Count-1 do
begin
if SpringList[Loop]<>RapidList[Loop]
then raise Exception.Create('Different content!');
end;
end,BenchRounds);
Writeln(Format('---------------------------------... %d',[Length(TestData)]));
Writeln('Iterate (for..in) through MormotList...');
RunBenchmark(
procedure
begin
var LastEntry: String;
for var Entry in MormotList do
begin
LastEntry:=Entry;
end;
end,BenchRounds);
Writeln('Iterate (for..in) through SpringList...');
RunBenchmark(
procedure
begin
var LastEntry: String;
for var Entry in SpringList do
begin
LastEntry:=Entry;
end;
end,BenchRounds);
Writeln('Iterate (for..in) through RapidList...');
RunBenchmark(
procedure
begin
var LastEntry: String;
for var Entry in RapidList do
begin
LastEntry:=Entry;
end;
end,BenchRounds);
Writeln(Format('---------------------------------... %d',[Length(TestData)]));
Writeln('Iterate (for..to) through MormotList...');
RunBenchmark(
procedure
begin
var LastEntry: String;
for var Loop:=0 to MormotList.Count-1 do
begin
LastEntry:=MormotList[Loop];
end;
end,BenchRounds);
Writeln('Iterate (for..to) through SpringList...');
RunBenchmark(
procedure
begin
var LastEntry: String;
for var Loop:=0 to SpringList.Count-1 do
begin
LastEntry:=SpringList[Loop];
end;
end,BenchRounds);
Writeln('Iterate (for..to) through RapidList...');
RunBenchmark(
procedure
begin
var LastEntry: String;
for var Loop:=0 to RapidList.Count-1 do
begin
LastEntry:=RapidList[Loop];
end;
end,BenchRounds);
end;
{ ----------------------------------------------------------------------- }
procedure TBenchmarks.RunBenchmark(const AProc: TProc; ABenchmarkRounds: Integer);
begin
var Stopper:=TStopwatch.StartNew;
for var Loop:=1 to ABenchmarkRounds do
begin
AProc;
end;
Stopper.Stop;
Writeln(Format('- (%d runs in %d msec.)',[ABenchmarkRounds,Stopper.ElapsedMilliseconds]));
Writeln;
end;
{ ----------------------------------------------------------------------- }
procedure TBenchmarks.Run;
begin
Writeln('---ListBenchmark---');
ListBenchmark;
Writeln('---DictBenchmark---');
Writeln('---DictBenchmark---');
Writeln('---DictBenchmark---');
DictBenchmark;
end;
{ ======================================================================= }
begin
try
var Benchmarks:=TBenchmarks.Create;
try
Benchmarks.Run;
finally
Benchmarks.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Writeln('Done. Press Enter for exit.');
Readln;
end.
And here is a result.
---ListBenchmark---
Populate MormotList with 100000 items...
- (100 runs in 275 msec.)
Populate SpringList with 100000 items...
- (100 runs in 245 msec.)
Populate RapidList with 100000 items...
- (100 runs in 241 msec.)
---------------------------------... 100000
Compare MormotList with SpringList...
- (100 runs in 214 msec.)
Compare MormotList with RapidList...
- (100 runs in 287 msec.)
Compare SpringList with RapidList...
- (100 runs in 285 msec.)
---------------------------------... 100000
Iterate (for..in) through MormotList...
- (100 runs in 353 msec.)
Iterate (for..in) through SpringList...
- (100 runs in 203 msec.)
Iterate (for..in) through RapidList...
- (100 runs in 285 msec.)
---------------------------------... 100000
Iterate (for..to) through MormotList...
- (100 runs in 102 msec.)
Iterate (for..to) through SpringList...
- (100 runs in 109 msec.)
Iterate (for..to) through RapidList...
- (100 runs in 236 msec.)
---DictBenchmark---
---DictBenchmark---
---DictBenchmark---
Populate MormotDict with 100000 items...
- (100 runs in 1216 msec.)
Populate SpringDict with 100000 items...
- (100 runs in 962 msec.)
Populate RapidDict with 100000 items...
- (100 runs in 573 msec.)
---------------------------------... 100000
Compare MormotDict with SpringDict...
- (100 runs in 848 msec.)
Compare MormotDict with RapidDict...
- (100 runs in 826 msec.)
Compare SpringDict with RapidDict...
- (100 runs in 483 msec.)
---------------------------------... 100000
Iterate MormotDict by key (TryGetValue)...
- (100 runs in 578 msec.)
Iterate SpringDict by key (TryGetValue)...
- (100 runs in 286 msec.)
Iterate RapidDict by key (TryGetValue)...
- (100 runs in 297 msec.)
---------------------------------... 100000
Iterate (for..in) MormotDict ...
- (100 runs in 662 msec.)
Iterate (for..in) SpringDict ...
- (100 runs in 402 msec.)
Iterate (for..in) RapidDict ...
- (100 runs in 611 msec.)
---------------------------------... 100000
Done. Press Enter for exit.
Hello Ab,
Thanks for you note.
Currently I use TObjectDictionary, and I would like to try TDynArrayHashed or TSynDictionary for better performance and if could save some memory.
As you said, that the TDynArrayHashed will not sort when add a new Item to the array? So that it will not re-sort when update the record? When a record is changed and need re-sort, how about use the SortRange for a better performance?
In my case the array will init only once then sort or sort full array when the user change the sort field he selected, and the items will update frequently so it need re-sort when every time a record filed get changed.
Would you advice to try the TDynArrayHashed or TSynDictionary or the plain TDynArray or just stay with TObjectDictionary?
Thank you
Just found that the TOnDynArraySortCompare is what I need.
Thanks
Hello,
I am try to use the TDynArrayHashed to store a long list of records. And would like to sort the Array base on the record fields. And the sort rule will base on field1, or field2 which is selected by the user.
R1 = record
f1: integer;
f2: string;
f3: double
end
So that how to pass the sort rule to the compare function?
TDynArraySortCompare = function(const A, B): integer;
/// event oriented version of TDynArraySortCompare
TOnDynArraySortCompare = function(const A, B): integer of object;
When I try to add the custom_record_comapre function to the TDynArrayHashed.Init function, The custom_record_comapre function can not be a Object function, so it can not access the sort_rule owned by the Object.
I have to define a global var of the Object or the sort_rule and use it in the custom_record_comapre function? But I have not only one Object different of them may sort by different record filed. Let say Object1 sort rule is record field1, Object2 sort rule is record field2.
Thank you.
Hello Thomas and ab,
Thank you for all your great works.
Since we added ToDouble function, why not also add a ToCurrency function too? They are almost the same.
And another matter which is not a must be feature, we have found that when we would like to skip some field, in this case if we only want 'A' and skip 'a', and set recValues = array[0..1] of TValuePUtf8Char;
pmvContext.ParseObject(['c', 'A'], @recValues)
We will get the 'c', 'a' values, not 'c', 'A'.
I also tried to change the props name in the rtti with
Rtti.ByTypeInfo[TypeInfo(TTestRec)].Props.NameChanges(['a2'], ['A']);
It seems that the above changes will not work.
I mean the parser will not care the Props Name's Letter case. If the Name is case sensitive, then the problems above should all get resolved.
Thank you very much.
@larand54
Sorry, I cut a small part from a long list of fields and forget to delete the COMMA at the end.
@Thomas
And do you know if it is possible to match a nested record such as by the DynArrayLoadJson in this case?
type
PTR1 = ^TR1;
TR1 = packed record
a1: double;
a2: double;
end;
TTestRec = packed record
c: double;
R1: PTR1;
end;
OR
TTestRec = record
c: double;
R1: TR1;
end;
TTestRecArray = array of TTestRec;
const
JSON = '[{"c": "28403.81000000","a": "28420.61000000","A": "0.00351000"},{"c": "0.13690000","a": "0.13930000","A": "408.00000000"}]';
var
recs: TTestRecArray;
As you see that we split the JSON text to be a nested record, and would like to DynArrayLoadJson the JSON directly to the recs array.
Thank you
Hello Thomas,
Thank you very much.
Hello ab, nice again.
I read a Json from a web service which return with a JSON array such as
[
{
"c": "28403.81000000",
"a": "28420.61000000",
"A": "0.00351000",
},
{
"c": "0.13690000",
"a": "0.13930000",
"A": "408.00000000",
}
]
When try DynArrayLoadJson to parse the Json text, it will not process the "a" and "A" name of data. Report a error
First chance exception at $769390E2. Exception class ERttiException with message 'Duplicated A property name'.
So that in this case there is not possible to use DynArrayLoadJson function? Will you consider to handle this?
Tough the data titile "a" and "A" is Duplicated consider by pascal language, but our purpose is to parse the data to a pre-defined record, even the name is Duplicated in the Json text, that will not matter.
Thank you.
Hello AB,
1, The wss is from the Chrome capture data, and My first aim is to get the ws:// without TLS to work.
2, I checked out the Project31SimpleEchoServer.dpr, It is the server part, You see my code posted here which try to get the client part to work, but not work out yet. As you focus on the interface implement, so that if I want to get a client to connect a Node.js server, I will need to work from the client standard webSockets protocol?
Thank you very much.
Hello AB,
Are you interesting this and can help with this? Websockets is popular now and mostly the wesockets servers are node.js or other python servers, so that we can use client part in Delphi to deal with them.
And big thank you for sharing the nice framework. Will try to learn more of it.
I added some new code try to send a frame to the server, but when I trace the code and get some access violation errors when ProcessMask , not sure why.
protocol := TWebSocketProtocolChat.Create('meow', '');
process := TMyWebSocketProcessClient.Create(Client.WebSockets, protocol, 'mORMotWebsocketClientProcess');
//Client.WebSockets.WebSockets:= process;
//process := Client.WebSockets.WebSockets;
frame.opcode := TWebSocketFrameOpCode.focText;
frame.content := [];
frame.payload := 'hello, test chat!';
if process.SendFrame(frame) then begin
memo1.lines.add('send frame success');
end;
The problem code is
PCardinalArray(data)^[i] := PCardinalArray(data)^[i] xor mask;
And SendFrame fail.
General
Request URL:wss://echo.websocket.org/?encoding=text
Request Method:GET
Status Code:101 Web Socket Protocol Handshake
Response Headers
view source
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:x-websocket-protocol
Access-Control-Allow-Headers:x-websocket-version
Access-Control-Allow-Headers:x-websocket-extensions
Access-Control-Allow-Headers:authorization
Access-Control-Allow-Headers:content-type
Access-Control-Allow-Origin:https://www.websocket.org
Connection:Upgrade
Date:Fri, 29 Sep 2017 15:49:54 GMT
Sec-WebSocket-Accept:N+kpt4PQBw7C/MeTZPzLHON+AeA=
Server:Kaazing Gateway
Upgrade:websocket
Request Headers
view source
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.8
Cache-Control:no-cache
Connection:Upgrade
Cookie:__zlcmid=ikgmcydUmlJ7z4; _ga=GA1.2.1394516097.1506699313; _gid=GA1.2.814511308.1506699313; _gat=1
Host:echo.websocket.org
Origin:https://www.websocket.org
Pragma:no-cache
Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
Sec-WebSocket-Key:ML8U6aK3xJQQTgdKUaHjlQ==
Sec-WebSocket-Version:13
Upgrade:websocket
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
Query String Parameters
view source
view URL encoded
encoding:text
After I watch the chrome connection to echo.websocket.org, from the data which show in the developer tools,
Now the echo.websocket.org server is waiting my client to send some headers to complete the handshake ?
And I dump the headers to memo1.
for I := Low(Client.WebSockets.Headers) to High(Client.WebSockets.Headers) do begin
memo1.Lines.Add(Client.WebSockets.Headers[ I]);
end;
From the headers, it shows
Connection: Upgrade
Date: Fri, 29 Sep 2017 15:19:00 GMT
Sec-WebSocket-Accept: PnZv2+arapwxuaf6vrXbBQYwBJc=
Server: Kaazing Gateway
Upgrade: websocket
So, the client should be upgrade to be a websockets connection with echo.websocket.org .
Then I do know how to send message and receive the data from the server.
After change the
Client.WebSocketsUpgrade('', true)
to
Client.WebSockets.WebSocketsUpgrade('/?encoding=text', '')
Then, It seems that I can upgrade the client to a websockets connection. But I still get the error string 'Invalid HTTP Upgrade Header' ;
Here is I get result in memo1.
Invalid HTTP Upgrade Header
echo.websocket.org
SockConnected true
Connection: Upgrade
Date: Fri, 29 Sep 2017 15:19:00 GMT
Sec-WebSocket-Accept: PnZv2+arapwxuaf6vrXbBQYwBJc=
Server: Kaazing Gateway
Upgrade: websocket
Hi, after review the code of Samples\31 - Project31ChatServer and Project31ChatClient, Do they implement the websockets standard? I think that they are not common websockets applications.
From the memo1, I get some result shows the upgrade failure,
Invalid HTTP Upgrade Header
echo.websocket.org
SockConnected true
<html><head></head><body><h1>404 WebSocket Upgrade Failure</h1></body></html>HTTP/1.1 501 Not Implemented
Hi,
I'm new of mORMot, now I try to build a application which receive the data from a web servers which use websockets as their method to delivery the data real time. Then I find that mORMot has some units which do many websockets works, so I would like to build my application base on the units of mORMot, Then after I looked all the demo of Samples 31, I see that all the websockets client implication base on the Interface with the mORMot websockets server. So I want to know if the units that I can build on them may create a websockets client application which can talk with the other websockets servers already built by others languages NOT base on the windows Interface technology?
I have tried some code and do not know how to do that, Can you help to get my test to work with the websockets site such as echo.websocket.org ? The demo server is https://www.websocket.org/echo.html .
Here I have some code, just get some errors with upgrade to websockets failed, What I need to add some raw header strings to get it to work?
procedure TForm2.Button6Click(Sender: TObject);
var Client: TSQLHttpClientWebsockets;
workName: string;
cmd, msg: SockString;
begin
Client := TSQLHttpClientWebsockets.Create('echo.websocket.org','80',TSQLModel.Create([]));
try
Client.Model.Owner := Client;
Client.WebSockets.Settings.SetFullLog;
// Client.WebSocketsUpgrade(PROJECT31_TRANSMISSION_KEY);
memo1.Lines.Add( Client.WebSocketsUpgrade('', true) );
try
memo1.Lines.Add(Client.WebSockets.Server);
if Client.WebSockets.SockConnected then
memo1.Lines.Add('SockConnected true');
Client.WebSockets.SockSend('Hello, websocket.org');
Client.WebSockets.SockSendFlush;
Client.WebSockets.SockRecvLn(msg);
memo1.Lines.Add(msg);
finally
//callback := nil;
//Service := nil; // release the service local instance BEFORE Client.Free
end;
finally
Client.Free;
end;
end;
Thank you.
Pages: 1