Performance problem upgrading an DA-server

Performance problem upgrading an DA-server

I’m in the process of upgrading an existing DA-Server (DA Version 7.0.65.1067 / Delphi XE) to DA Version10.0.0.1543 on Delphi XE 10.2.

I’ve tried to minimize source changings to as few as possible. While the new server compiles and runs, I found that the performance is very, very poor now. Some, but not all, calls are at least more than 10 times slower than before. Both servers use SuperTCP connection.

As far as I found out up to now, it has to do with the “per Client” Service needed for some reasons but I can’t find out a difference between the old the new one.

I know it is hard to say from outside, but any suggestion where to start to find the bottleneck?

Hi,

Pls review Breaking Changes article. it may contain some useful info for you.

of course, it would be great to migrate from DAv7 to DAv10 in Delphi XE and compare performance in the same version of Delphi because some users were complained that the same code works worse in modern versions of Delphi.

A the moment I can offer the following Client LOG entries:
(first column is GetTickCount)

Timing old Server:
1977617484 04.11.2022 12:57:45 GetIconID running…
1977617500 04.11.2022 12:57:45 GetIconID done.
1977617500 04.11.2022 12:57:45 GetSlotState(0,4,6,15)
1977617500 04.11.2022 12:57:45 GetSlotState running…
1977617500 04.11.2022 12:57:45 Get^SlotState done.
1977617515 04.11.2022 12:57:45 GetIconID running…
1977617531 04.11.2022 12:57:45 GetIconID done.
1977617531 04.11.2022 12:57:45 GetSlotState(0,4,6,14)
1977617546 04.11.2022 12:57:45 GetSlotState running…
1977617546 04.11.2022 12:57:45 Get^SlotState done.
38 ticks für 2 GetIconID / 2 GetSlotState

Timing new Server:
1974365000 04.11.2022 12:03:33 GetIconID running…
1974365265 04.11.2022 12:03:33 GetIconID done.
1974365265 04.11.2022 12:03:33 GetSlotState(0,4,0,47)
1974365281 04.11.2022 12:03:33 GetSlotState running…
1974365531 04.11.2022 12:03:34 Get^SlotState done.
1974365531 04.11.2022 12:03:34 GetSlotState(0,4,0,47)
1974365546 04.11.2022 12:03:34 GetSlotState running…
1974365781 04.11.2022 12:03:34 Get^SlotState done.
781 ticks für 1 GetIconID / 2 GetSlotState

Timing new Server -without workload code
1988009218 04.11.2022 15:50:56 GetIconID running…
1988009453 04.11.2022 15:50:56 GetIconID done.
1988009453 04.11.2022 15:50:56 GetSlotState(0,4,14,36)
1988009453 04.11.2022 15:50:56 GetSlotState running…
1988009687 04.11.2022 15:50:57 Get^SlotState done.
1988009687 04.11.2022 15:50:57 GetIconID running…
1988009953 04.11.2022 15:50:57 GetIconID done.
735 ticks für 2 GetIconID / 1 GetSlotState

As you can see, the new server is about 20 times slower then the old one, even if I remove all “workload code” :frowning:

Because I can’t compile the DA-Version on my old Delphi XE System, it is hard to see if it comes form the “modern Delphi” or the “modern DA”…

Hi,

what issues do you have with using DA v10 in Delphi XE?

what exactly SuperTCP implementation are you using? we have Synapse-, Indy-, Socket-based implementations.

If you used Indy-based implementation in DAv7, check that you still use it:

  • Indy prefix was added to Indy Super TCP channels:
    • TROSuperTCPServerTROIndySuperTCPServer
    • TROSuperTCPChannelTROIndySuperTCPChannel

Hmm, I’m still using: unit uROSuperTcpServer TROSuperTcpServer. Its still there – at least on my system. ROVersion.inc shows: RO_VERSION = ‘10.0.0.1543’;
Shoud I change it to TRO Indy SuperTCPServer?

No issues – I simply don’t have a setup with DAv10 and Delphi XE

Hi,

Yes, change. it may improve performance.

Done – unfortunately it doesn’t help :frowning:
I think I’ve to go a step back and check performance with a simple example. Do you have a test case? Maybe I can use one of the examples. Which one would you suggest?

Hi,

You can use the MegaDemo sample

OK, thought of an DataAbstract example, but maybe it’s better to start with SDK…
But before I dig into it, here is another LOG, where I´ve combined Client and Server LOG using TickCount (C: marked from Client- / S:marked from Server LOG)

GetTickCount Message                           deltaT  Remark
2234128234   C: GetSlotState running
2234128250   S: GetSlotState(0,4,5,39)running...  16      Client -> Server
2234128250   S: GetSlotState  done.                0      Server Workload
2234128515   C: GetSlotState  done.              265      Server -> Client

As you can see the “return from server to client” task takes 265 ticks, while calling the server from client is only 16 ticks. How comes? Where to look?

Hi,

Can you attach a simple testcase what can I review, pls?
I don’t know where in code you call your log method.

Trying to do so. I’ve being using your wizard building a minimal RO client / server. Starting the server fails with an AV in:

uRORTTIServerSupport.TRORttiAttributeHelper.ROGetAttribute<uRORTTIAttributes.ROZeroConfServiceAttribute>

Don’t know where and why ROZeroConf comes in… How can I avoid this?

Server site: First / last statement of my implementation method in MyService_impl
Client site: right before and after calling the service method.

Hi,

hmm, I can’t reproduce it. what exactly project are you created?

you can try to disable/comment these lines:

  • CodeFirst-based project:
  [ROZeroConfService(__ServiceName)]
  • RODL-based project:
   RegisterForZeroConf(fClassFactory_NewProjectService, '_NewProjectService_rosdk._tcp.');

better to put your code into the OnReadFromStream/OnWriteToStream events of channel and server components

VCL Standalone (RODL Based).
Commenting “RegisterForZeroConf” doesn’t help. Call-Stack is:

uRORTTIServerSupport.TRORttiAttributeHelper.ROGetAttribute<uRORTTIAttributes.ROZeroConfServiceAttribute>
uRORTTIServerSupport._do($2874F60)
uRORTTIServerSupport.GetCodeFirstZeroConfNames(TROStrings($282C374) as IROStrings,‘TestMinROServeService’)
uROServer.TROServer.RebuildServices
uROServer.TROServer.SetActive(True)
fServerForm.TServerForm.FormCreate($27D3D00)
:005d5563 TCustomForm.DoCreate + $37
:005d517f TCustomForm.AfterConstruction + $17
:005d5130 TCustomForm.Create + $1B0
:005e0691 TApplication.CreateForm + $79
TestMinROServeServer.TestMinROServeServer
:75c76739 KERNEL32.BaseThreadInitThunk + 0x19
:77298fd2 ;
:77298f9d ;

Hi,

Can you attach your testcase, pls?
you can drop email to support@ for keeping privacy.

done.
For now I’ve compared the Call-Stack of the MegaSample-Server and mine:
MegaSample looks like this:
MEGADEMO with PerClientFacrory:

MegaDemoService_Impl.TMegaDemoService.Sum(1,2)
MegaDemoLibrary_Invk.TMegaDemoService_Invoker.Invoke_Sum(TMegaDemoService($2897220) as IInterface,TROServerMultiMessage($2854454) as IROMessage,TROSCServerWorker($28C2620) as IROTransport,)
uROServer.TROInvoker.CustomHandleMessage(TROPerClientClassFactory($295549C) as IROClassFactory,TROServerMultiMessage($2854454) as IROMessage,TROSCServerWorker($28C2620) as IROTransport,)
:

My Server shows the following call stack:

SYC_CPBuffer_Impl.TSYC_CPBuffer.DoScan(42205)
System.Rtti.RawInvoke(???,???)
System.Rtti.Invoke($1021084,((($101FFBC, Pointer($1044DAC) as IValueData, 48, 40752, 106995504, $6609F30, TClass($6609F30), 48, -24784, 106995504, 4,2246670037828e-35, 5,28628027858697e-316, 0,00000000039002e-4933, 106995504, 10699,5504, 106995504, 106995504, ($6609F30, nil), $6609F30)), (($402D1C, Pointer($1044DAC) as IValueData, 0, 0, 0, nil, nil, 0, 0, 0, 0, 42205, 1,70450748504715e-4932, 4,67603342461449e+18, 467603342461449, 4676033424614490112, 4676033424614490112, (nil, $40E49BA0), nil))),ccReg,nil,False,False)
System.Rtti.TRttiInstanceMethodEx.DispatchInvoke((($101FFBC, Pointer($1044DAC) as IValueData, 48, 40752, 106995504, $6609F30, TClass($6609F30), 48, -24784, 106995504, 4,2246670037828e-35, 5,28628027858697e-316, 0,00000000039002e-4933, 106995504, 10699,5504, 106995504, 106995504, ($6609F30, nil), $6609F30)),(…))
System.Rtti.TRttiMethod.Invoke($6609F30,(…))
uRORTTIServerSupport.TRORTTIInvoker.RTTIInvoke(TSYC_CPBuffer($660A010) as IInterface,TROBinMessage($5D33FA4) as IROMessage,TROSCServerWorker($5566410) as IROTransport,)
**uROServer.TROInvoker.CustomHandleMessage(**TROPerClientClassFactory($5617FBC) as IROClassFactory,TROBinMessage($5D33FA4) as IROMessage,TROSCServerWorker($5566410) as IROTransport,)
:

Up to TROInvoker.CustomHandleMessage it is same. After that RTTI is invoked on my server. I guess that is where the problems come from, but don’t know how to avoid this.

Hi,

it works w/o AV on my side :frowning:


the MegaDemo sample is RODL-based project so it uses TMegaDemoService_Invoker.Invoke_Sum.

in your case you have CodeFirst-based project as a result, it hasn’t personal Invoker class and uses RTTI methods for calling DoScan method.

if you converted your project to CodeFirst, check backup subfolder - it contains old RODL-based files …

Thought so - should run here :upside_down_face:

How can I get rid of that modern DA CodeFirst feature? Don’t think I have the original RODL-based files anymore :face_with_hand_over_mouth:

Hi,

if you don’t have __Backup subfolder you can launch your .exe and generate .RODL in runtime.

note: You can get RODL in ServiceBuilder.

this RODL can be used for generating _Intf, _Invk and _Impl files in ServiceBuilder

later you can move your logic from CodeFirst _Impl to RODL-based _Impl.

Hoped for a tool, but OK, I still have the RODL files. Will take some time…

Rebuilded my RO-SDK Services as RODL based. While it looks, at least for me, much cleaner now and call stack doesn’t show RTTI overhead anymore it doesn’t help so much. Speed is still way to slow. :frowning:

But I think I found the show stopper now: It is the DataAbstractServiceDeactivate event handler that calls Self.Connection.Close which seems to add some 250 ms to every round trip which is unacceptable in some situations. Out commenting “connection.close” does the trick. :blush:

Remaining problem is that without Connection.Close I get an RTE when the application is stopped. What is a clean way to close the connection?