views:

818

answers:

3

I'm researching a bug that looks like some kind of timing issue and so I'm a bit curious about how events work in Delphi 7. What happens is we get some data sent to our application through a COM interface and it gets handled in an event raised from the COM thread. It seems like the event, which has quite a bit of code in it, takes longer and longer to execute and after a while the entire application crashes. There are calls to graphics and stuffing into large arrays inside the event that might affect time. I have been unable to spot any significant increase in memory usage and have not had opportunity to run any profilers to check for leaks yet. Also, the obvious thing to test would be to strip the event of all the code in it just to see if we can run for a longer period of time.

Are events serial or parallell in Delphi, that is, if I get a new event while one is executing -what happens? Is it run in parallell on some kind of automatic thread, is it ignored or is it queued up?

If it is queued up, how many can I have in the queue before the application crashes?

Does indexing into a large array take longer the further into it you are? Even if it's of a fixed size? I don't think it should so I'm looking for leaks and allocations that take time. If I get sent an object through the event, should I dispose of it within the event or in the "calling" code?

What things usually do not scale well in Delphi? What can I look for that would increase in execution time?

Finally, since this is COM related, any pointers to common pitfalls in COM are appreciated although I realize this is tricky. I do have a grip on co-initialize though.

A: 

Did some research and got a few pointers, particularly to my first question:

Are events serial or parallell in Delphi, that is, if I get a new event while one is executing -what happens? Is it run in parallell on some kind of automatic thread, is it ignored or is it queued up? If it is queued up, how many can I have in the queue before the application crashes?

Well, obviously events are as synchronous as in everything else, or should I say serial. You can not get more events as you process one since that event essentially is a function call.

In the event handler, some graphic components are handled. Since the event is raised on another thread this is bad. I either need to make some updating mechanism on the graphics that lies on the thread that created the graphics or make a thread switch in the event.

Also, tests indicate that it in fact is the update of the graphics that is taking increasingly longer time, thus a refactoring of the graphics handling sounds like a good path to try at first.

Niklas Winde
+2  A: 

Delphi is mostly serial in handling events. Unfortunately, it is possible to tell Delphi to handle other events while you're running in some event already. As a result, new events will run while your current event is waiting for the new event to finish. In the worst case, your application might appear to behave normally while it's really stacking events within events within events. Rule one: avoid using Application.ProcessMessages unless you really need this.

When using COM objects, things become a bit more complex since the COM object might have it's own events, start it's own threads and do all other kinds of things that you don't have any control over. COM appears to be easy to use in Delphi but it does have many hidden pitfalls for the inexperienced developers. (I still have the scars after surviving a few of them!)

In general, when working with COM objects, I try to separate the COM calls in their own threads, creating special components that will keep the COM object within it's own thread and adding a lot of synchronization code, just so I can keep the GUI responsive while some long COM task is doing some processing. But doing this requires a lot of experience with COM and multi-threading. But basically, designing your custom wrapper component around any COM component is good practice, just to protect the resources that your COM class needs.

Delphi's strongest weakness tends to be string handling and the handling of huge arrays. (Especially arrays that contain objects; use records instead.) Strings themselves are fast in Delphi, but the string functions in Delphi are not very optimized. For example, I once had a string containing some XML data. It had a lot of boolean fields that were spelled like "True" and "False" and needed to convert them to "true" and "false". A simple string-replace took about 15 seconds to replace all these values. The I rewrote it by using MSXML to load the XML in a DOM Document, using XPath to select all boolean nodes, loop through those nodes to replace all the values with the proper texts and then put the XML back into a single string. Suddenly it was able to do the same within two seconds! A huge performance gail for something that appeared to be slower. The reason? When Delphi is handling strings, it tends to copy the string a few times during processing. Or it needs to allocate more and more memory for the string to increase in size. This takes time, which doesn't get wasted in some other languages like C++.

Workshop Alex
I completely disagree with your last paragraph. Naive == slow string handling can be implemented in every language, certainly in C++ as well. With a proper algorithm to lower-case True and False in a string it should be possible to process a few MByte per second. Going from 15 seconds to 2 seconds if (depending on the string length) maybe 10 or 100 ms are possible? I don't know...
mghie
Feel free to disagree, but I was only using the standard functionality of Delphi. I have enough experience to know how to implement a lot better string handling in Delphi. But having to rewrite existing code with more optimized code isn't something I like to do so I prefer to pick the easiest solutions. In this case, by using MSXML.I once competed with a C++ developer in writing a textfile parser. In the end, I managed to get Delphi code that performed as fast as the C++ code, but I had to use PChar's and several other kinds of performance tricks to get this result.
Workshop Alex
A: 

The important thing for COM is what "apartment model" your application is supporting. The most commonly used in Delphi is the Single Threaded Apartment model also known as "Apartment threaded" where COM calls are synchronized with your applications main message queue. With this model you would only be able to process a single COM call at a time and COM objects don't support calls from other threads.

However, you can initialize your COM apartment model to be multithreaded and in doing so you will have to make sure access to shared resources are properly synchronized - each thread you start will have to join the multithreaded apartment by calling CoInitializeEx(nil, COINIT_MULTITHREADED);

Things get really interesting when you expose DCOM interfaces as the RPC subsystem has a thread pool for servicing requests which can directly access all your COM objects in the multithreaded apartment allowing for a high performance server. If you are using apartment threading you will have to go through the bottleneck of the message queue and the single thread can only dispatch one COM call at a time.

Chris Bensen wrote a nice blogpost about this too with some code samples.

Christer Fahlgren