I have objects inherited from different basic objects that have certain strong typed methods like
method CellEdit (oEvent as bcelleditevent) as logic strict
I cannot add a prototype to the basic classes.
For performance and stability reasons I would like to call them with a sort of delegate if possible. Can someone give me an example and is it worth to do this if the method is often called?
Thanks
Arne
Calling a strong typed method for an unknown object for performance reasons
- ArneOrtlinghaus
- Posts: 412
- Joined: Tue Nov 10, 2015 7:48 am
- Location: Italy
Calling a strong typed method for an unknown object for performance reasons
Hi Arne,
It is not clear to me if you are doing early or late bound calls to those methods. I realize they base ones have CLIPPER calling convention, but do you call them early bound with SUPER() for example? In this case, I think the call itself is already fast enough. With late bound calls it should be possible to improve it, but can you please post a sample usage of our code?
As for if it's worth looking into this, well unless a method is being called a lot of thousands of times per second, I don't think the time it takes to make the call itself is ever noticeable. Far far more important is the performance of the code that is being executed inside the method body itself, especially if we are talking about UI stuff.. Well, that is unless you are implementing some mathematical algorithm with all sorts of jumps etc, in which case it can make a big difference!
Chris
It is not clear to me if you are doing early or late bound calls to those methods. I realize they base ones have CLIPPER calling convention, but do you call them early bound with SUPER() for example? In this case, I think the call itself is already fast enough. With late bound calls it should be possible to improve it, but can you please post a sample usage of our code?
As for if it's worth looking into this, well unless a method is being called a lot of thousands of times per second, I don't think the time it takes to make the call itself is ever noticeable. Far far more important is the performance of the code that is being executed inside the method body itself, especially if we are talking about UI stuff.. Well, that is unless you are implementing some mathematical algorithm with all sorts of jumps etc, in which case it can make a big difference!
Chris
Chris Pyrgas
XSharp Development Team
chris(at)xsharp.eu
XSharp Development Team
chris(at)xsharp.eu
- ArneOrtlinghaus
- Posts: 412
- Joined: Tue Nov 10, 2015 7:48 am
- Location: Italy
Calling a strong typed method for an unknown object for performance reasons
Hi Chris,
there are many notification methods that until now are called late bound from other objects. For example the data browser (bbrowser) uses many of those methods calling a method of the owner of the browser. Displaying larger amounts with many colors, column info etc. is still a bit slow. Until now the methods are called late bound like self:Owner:Celledit (oEvent).
I have converted the methods called to strong typed methods. Now I have asked me if I could use a type of reflection to get a pointer or delegate to the method. I cannot use the Interface pattern and I cannot insert a dummy method in the super classes.
there are many notification methods that until now are called late bound from other objects. For example the data browser (bbrowser) uses many of those methods calling a method of the owner of the browser. Displaying larger amounts with many colors, column info etc. is still a bit slow. Until now the methods are called late bound like self:Owner:Celledit (oEvent).
I have converted the methods called to strong typed methods. Now I have asked me if I could use a type of reflection to get a pointer or delegate to the method. I cannot use the Interface pattern and I cannot insert a dummy method in the super classes.
Calling a strong typed method for an unknown object for performance reasons
Hi Arne,
Ah ok, since we are talking about late bound call, then strongly typing the methods unfortunately does not make the calls faster. It would still help showing a 20 line cut down sample of the involved calls though, as I am still not sure what parts of the code you can modify and what not. In the Owner:CellEdit() code, do you have any information about what "Owner" is at compile time? If all owners inherit from a base class that as a CellEdit() method for example, you can cast the call to an early bound call in this class. It is also possible to write some dedicated IL code that makes some calls, if you suspect it's a specific call that causes the slowness. Are you thinking about optimizing calls to a specific method, or to many?
For example, assuming the CellEdit() method has this signature:
METHOD CellEdit(oEvent AS bCellEditEvent) AS LOGIC
then you can use the following function to call it in a fast way through a dynamic method. You would only need to change your calls from:
self:Owner:Celledit (oEvent)
to:
InvokeCellEdit(self:Owner , oEvent)
and this should make the call itself at least 10 times faster than before. But I do not know if this will have a noticeable difference, or it is the execution of the method body that actually causes the delay and not the call. But anyway, here's the code and if you think something that this can help, I will give you some more info on how to modify it to call other methods as well:
#using System.Reflection.Emit
#using System.Collections.Generic
// same as the method, plus instance of object to call method at
DELEGATE CellEditDelegate(obj AS OBJECT, oEvent AS bCellEditEvent) AS LOGIC
FUNCTION InvokeCellEdit(obj AS OBJECT , oEvent AS bCellEditEvent) AS LOGIC
LOCAL oDelegate AS CellEditDelegate
LOCAL oDynamic AS DynamicMethod
LOCAL oIL AS ILGenerator
// cache of the dynamic methods we create, one per different object instance
STATIC LOCAL aDelegates AS Dictionary<OBJECT,CellEditDelegate>
IF aDelegates == NULL
aDelegates := Dictionary<OBJECT,CellEditDelegate>{}
END IF
IF aDelegates:ContainsKey(obj)
oDelegate := aDelegates[obj]
ELSE
// (dummy) name, return type, parameters
oDynamic := DynamicMethod{"CellEdit()" , typeof(LOGIC) , <Type>{TypeOf(OBJECT),TypeOf(bCellEditEvent)} , obj:GetType():Module}
oIL := oDynamic:GetILGenerator()
oIL:Emit(OpCodes.Ldarg_0) // object instance
oIL:Emit(OpCodes.Ldarg_1) // the oEvent argumet
oIL:Emit(OpCodes.Call , obj:GetType():GetMethod("CellEdit"))
oIL:Emit(OpCodes.Ret)
oDelegate := (CellEditDelegate)oDynamic:CreateDelegate(TypeOf(CellEditDelegate))
aDelegates:Add(obj , oDelegate)
END IF
RETURN oDelegate:Invoke(obj , oEvent)
Chris
Ah ok, since we are talking about late bound call, then strongly typing the methods unfortunately does not make the calls faster. It would still help showing a 20 line cut down sample of the involved calls though, as I am still not sure what parts of the code you can modify and what not. In the Owner:CellEdit() code, do you have any information about what "Owner" is at compile time? If all owners inherit from a base class that as a CellEdit() method for example, you can cast the call to an early bound call in this class. It is also possible to write some dedicated IL code that makes some calls, if you suspect it's a specific call that causes the slowness. Are you thinking about optimizing calls to a specific method, or to many?
For example, assuming the CellEdit() method has this signature:
METHOD CellEdit(oEvent AS bCellEditEvent) AS LOGIC
then you can use the following function to call it in a fast way through a dynamic method. You would only need to change your calls from:
self:Owner:Celledit (oEvent)
to:
InvokeCellEdit(self:Owner , oEvent)
and this should make the call itself at least 10 times faster than before. But I do not know if this will have a noticeable difference, or it is the execution of the method body that actually causes the delay and not the call. But anyway, here's the code and if you think something that this can help, I will give you some more info on how to modify it to call other methods as well:
#using System.Reflection.Emit
#using System.Collections.Generic
// same as the method, plus instance of object to call method at
DELEGATE CellEditDelegate(obj AS OBJECT, oEvent AS bCellEditEvent) AS LOGIC
FUNCTION InvokeCellEdit(obj AS OBJECT , oEvent AS bCellEditEvent) AS LOGIC
LOCAL oDelegate AS CellEditDelegate
LOCAL oDynamic AS DynamicMethod
LOCAL oIL AS ILGenerator
// cache of the dynamic methods we create, one per different object instance
STATIC LOCAL aDelegates AS Dictionary<OBJECT,CellEditDelegate>
IF aDelegates == NULL
aDelegates := Dictionary<OBJECT,CellEditDelegate>{}
END IF
IF aDelegates:ContainsKey(obj)
oDelegate := aDelegates[obj]
ELSE
// (dummy) name, return type, parameters
oDynamic := DynamicMethod{"CellEdit()" , typeof(LOGIC) , <Type>{TypeOf(OBJECT),TypeOf(bCellEditEvent)} , obj:GetType():Module}
oIL := oDynamic:GetILGenerator()
oIL:Emit(OpCodes.Ldarg_0) // object instance
oIL:Emit(OpCodes.Ldarg_1) // the oEvent argumet
oIL:Emit(OpCodes.Call , obj:GetType():GetMethod("CellEdit"))
oIL:Emit(OpCodes.Ret)
oDelegate := (CellEditDelegate)oDynamic:CreateDelegate(TypeOf(CellEditDelegate))
aDelegates:Add(obj , oDelegate)
END IF
RETURN oDelegate:Invoke(obj , oEvent)
Chris
Chris Pyrgas
XSharp Development Team
chris(at)xsharp.eu
XSharp Development Team
chris(at)xsharp.eu
Calling a strong typed method for an unknown object for performance reasons
Hi Arne,
i would, if I understood you right, define an interfact and implement this interface by the different classes which could be used here. And instead of using object as type use the interface instead. Then you have the best performance and! type safety.
Regards
Meinhard
i would, if I understood you right, define an interfact and implement this interface by the different classes which could be used here. And instead of using object as type use the interface instead. Then you have the best performance and! type safety.
Regards
Meinhard
- ArneOrtlinghaus
- Posts: 412
- Joined: Tue Nov 10, 2015 7:48 am
- Location: Italy
Calling a strong typed method for an unknown object for performance reasons
Hi Meinhard,
I though already on this.
I ask me if I have to mark every of these objects as "implements xy". If yes, then it is a problem in converting our old code. If no, then it would be a nice solution.
Arne
I though already on this.
I ask me if I have to mark every of these objects as "implements xy". If yes, then it is a problem in converting our old code. If no, then it would be a nice solution.
Arne
- ArneOrtlinghaus
- Posts: 412
- Joined: Tue Nov 10, 2015 7:48 am
- Location: Italy
Calling a strong typed method for an unknown object for performance reasons
I must admit:
Calling strong typed functions is good in general using normal object inheritances. Trying to optimize single calls helps only in cases where a method is called many times per second.
I have now spent quite a lot of time in optimization using the Telerik Just Trace Performance Analyzer.
Using the Performance Analyzer I could optimize many of the most often called functions/methods. For example optimizing the Fieldpos method of our SQL objects gave about 1% of performance increase.
Strong typing the GUI events gave some percent in performance increase.
And then there are often real stoppers in the programs like a Procline() or a Procname command that have to be resolved.
What is difficult to change are the losses of performance of usuals, symbols, arrays.
And it is normally the combination of arrays with usuals/symbols that consume much more time running in Dot.net than in VO. A comparison with a symbol is expensive. In VO symbols were numbers that were filled once at the beginning and could be compared quickly. With the Vulcan runtime every assignment to a symbol or comparion with a symbol makes a call to an internal dictionary. So Symbol treating in Dot.net consumes at least 1% in our programs.
Interesting that the Vulcan function VAL is also a good candidate for optimizations. Currently it consumes about 1% of the time in our programs, mostly for converting SQL Data that returned as a string from the ODBC driver.
Now the performance of our programs in Dot.net is not so bad. In average it is still 5 to 10% percent behind the VO version when I exclude the program items with "stoppers" to optimize.
What is really nice are the possibilities for optimizations in Dot.net.
Starting a performance analyzer like Telerik Just Trace, running the program without changes for a while and then being able to analyze the output is a nice and efficient task.
Calling strong typed functions is good in general using normal object inheritances. Trying to optimize single calls helps only in cases where a method is called many times per second.
I have now spent quite a lot of time in optimization using the Telerik Just Trace Performance Analyzer.
Using the Performance Analyzer I could optimize many of the most often called functions/methods. For example optimizing the Fieldpos method of our SQL objects gave about 1% of performance increase.
Strong typing the GUI events gave some percent in performance increase.
And then there are often real stoppers in the programs like a Procline() or a Procname command that have to be resolved.
What is difficult to change are the losses of performance of usuals, symbols, arrays.
And it is normally the combination of arrays with usuals/symbols that consume much more time running in Dot.net than in VO. A comparison with a symbol is expensive. In VO symbols were numbers that were filled once at the beginning and could be compared quickly. With the Vulcan runtime every assignment to a symbol or comparion with a symbol makes a call to an internal dictionary. So Symbol treating in Dot.net consumes at least 1% in our programs.
Interesting that the Vulcan function VAL is also a good candidate for optimizations. Currently it consumes about 1% of the time in our programs, mostly for converting SQL Data that returned as a string from the ODBC driver.
Now the performance of our programs in Dot.net is not so bad. In average it is still 5 to 10% percent behind the VO version when I exclude the program items with "stoppers" to optimize.
What is really nice are the possibilities for optimizations in Dot.net.
Starting a performance analyzer like Telerik Just Trace, running the program without changes for a while and then being able to analyze the output is a nice and efficient task.
- ArneOrtlinghaus
- Posts: 412
- Joined: Tue Nov 10, 2015 7:48 am
- Location: Italy
Calling a strong typed method for an unknown object for performance reasons
Just to finish what I am thinking:
I have reached nearly a point where better performance is getting expensive because of having to rewrite much code.
Now I am looking for another field that may help improve performance:
More buffering of data.
As Dotnet memory handling is much better than VO memory handling I will look which SQL Data I can buffer. As with reading of SQL information most of the time is spent this seems to be the best way to improve the programs. I have seen that the generic dictionaries give great possibilities.
I have reached nearly a point where better performance is getting expensive because of having to rewrite much code.
Now I am looking for another field that may help improve performance:
More buffering of data.
As Dotnet memory handling is much better than VO memory handling I will look which SQL Data I can buffer. As with reading of SQL information most of the time is spent this seems to be the best way to improve the programs. I have seen that the generic dictionaries give great possibilities.
Calling a strong typed method for an unknown object for performance reasons
Hi Arne,
Thanks for posting your findings in detail! The results are close to what I was expecting, too.
Regarding VO-specific data types being slower in .Net compared to VO, this of course happens because in VO those are sort of native types, while in .Net they have to be emulated with custom types. In addition, their implementation in the vulcan runtime is not optimal, we will improve it a lot in the x# runtime which should be released later this year.
Regarding Val(), as we talked about also in private email, I think the reason why it is not executing fast is because it uses a regular expression to parse the string value, which although is a very neat solution, it is probably overkill for the 5-6 character long strings that are usually being passed to Val(). We'll take care of this, too.
Chris
Thanks for posting your findings in detail! The results are close to what I was expecting, too.
Regarding VO-specific data types being slower in .Net compared to VO, this of course happens because in VO those are sort of native types, while in .Net they have to be emulated with custom types. In addition, their implementation in the vulcan runtime is not optimal, we will improve it a lot in the x# runtime which should be released later this year.
Regarding Val(), as we talked about also in private email, I think the reason why it is not executing fast is because it uses a regular expression to parse the string value, which although is a very neat solution, it is probably overkill for the 5-6 character long strings that are usually being passed to Val(). We'll take care of this, too.
Chris
Chris Pyrgas
XSharp Development Team
chris(at)xsharp.eu
XSharp Development Team
chris(at)xsharp.eu
Calling a strong typed method for an unknown object for performance reasons
Hi Arne,
since memory is cheap these days, I'm using more and more caching in my programs. Things like the VAT tables, payment conditions and all the myriad of small tables that are read over and over I'm reading in a type of buffered server. I've attached one of my cache server classes (CachedServer), tied to DBF files as it compares the time stamp of the DBF file and discards its complete contents if it has changed. With SQL, maybe you need to implement a flag system.
I have also another class, CacheServer, that caches only a few (configured) fields, and only records that have already needed. I don't think this would works well with SQL, but I have attached this class also.
Wolfgang
since memory is cheap these days, I'm using more and more caching in my programs. Things like the VAT tables, payment conditions and all the myriad of small tables that are read over and over I'm reading in a type of buffered server. I've attached one of my cache server classes (CachedServer), tied to DBF files as it compares the time stamp of the DBF file and discards its complete contents if it has changed. With SQL, maybe you need to implement a flag system.
I have also another class, CacheServer, that caches only a few (configured) fields, and only records that have already needed. I don't think this would works well with SQL, but I have attached this class also.
Wolfgang
- Attachments
-
- CacheServer_Classes.zip
- (2.83 KiB) Downloaded 76 times
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it