The first two systems I worked with using InterSystems technology were a PDP-11 running M11+ and a VAX 11/750 running M/VX. Too many years ago to count! 😊
Since then I've used most, if not all, InterSystems products up to IRIS and HealthShare today.
I'm Italian living in Switzerland and I work as Senior Consultant at GAIVOTA consultin SA, we provide professional services for InterSystems and other technologies.
Curiosity: apart from DC, I don't have ANY social account! 😁
To mesure the difference I've a differente approach then Robert and concentrate on same/different class calls without any additional overhead.
Please note that is my no means a demonstration of best/worst approach, IMHO there is a better solution, see my next post.
I've created two classes and simulated a big number of calls of a class method within the same class and same code calling a class method in a different class.
Class Community.perf.Class1
{
ClassMethod Compare(NumCalls As %Integer)
{
Set SingleStart=$zh
Do ..SingleClassCalls(NumCalls)
Set SingleEnd=$zh
Set MultiStart=$zh
Do ..MultiClassCalls(NumCalls)
Set MultiEnd=$zh
Set Difference=(MultiEnd-MultiStart)-(SingleEnd-SingleStart)
Set Percent=1-((SingleEnd-SingleStart)/(MultiEnd-MultiStart))*100
Write "Same class calls: ",SingleEnd-SingleStart,!
Write "Diff class calls: ",MultiEnd-MultiStart,!
Write "Difference: ",Difference," ",Percent,"%",!
}
ClassMethod SingleClassCalls(NumCalls As %Integer)
{
For i=1:1:NumCalls {
Set x=..Compute(NumCalls)
}
}
ClassMethod MultiClassCalls(NumCalls As %Integer)
{
For i=1:1:NumCalls {
Set x=##class(Community.perf.Class2).Compute(NumCalls)
}
}
ClassMethod Compute(Num As %Integer)
{
;Quit Num
Set ret=Num
Quit ret
}
}
Class Community.perf.Class2
{
ClassMethod Compute(Num As %Integer)
{
;Quit Num
Set ret=Num
Quit ret
}
}
Calling the Compare() method with 100M iterations the result is:
EPTEST>do ##class(Community.perf.Class1).Compare(100000000)
Same class calls: 20.992929
Diff class calls: 31.460201
Difference: 10.467272 33.27147210534351%
Please note that changing the Compute() method in both classes to:
ClassMethod Compute(Num As %Integer)
{
Quit Num
}
Makes a BIG difference:
EPTEST>do ##class(Community.perf.Class1).Compare(100000000)
Same class calls: 4.606181
Diff class calls: 5.52639
Difference: .920209 16.6511773508565266%
Using the first method code it adds the handling of the stack, therefore it takes longer and, for 100M calls, the difference is noticeable.
I think the first with "some" stack handling is a more realistic use case.
In conclusion, there is a difference and is measurable. Is it noticeable? Not much, IMHO in a computation that probably takes many minutes duplicating codes is not worth the gain.
There is however a better approach without duplicating the code, see my next post.
Ciao Robert,
I'm not sure your test address the original question, that is:
"whenever you call code outside of the routine as opposed to calling code in the same routine, some execution speed is lost"
In your test1 you do:
[call class method] -> [call class method in other class] -> [call a function in same class]
In your test2 you do:
[call class method] -> [call a function in same class]
This way you are not comparing "call code outside of the routine as opposed to calling code in the same routine", you are ADDING an additional call/level.
In addition, to measure the time penalty for calling inside/outside "routine" (or class), adding 100M global access does not help in getting a reliable measure because too many factors may change the time measured between different runs.
Finally a doubt, are we sure that calling a function and calling a class method is a fair comparison? May be or may be not, I don't know.












IMHO the best approach is to take advantage of the object-oriented development environment that IRIS provide and have the common functions/methods in a single (or multiple) classes, possible abstract classes, and inherit them in the "main" class.
Class Community.perf.ClassMain Extends Community.perf.ClassAbs { ClassMethod Compare(NumCalls As %Integer) { Set Start=$zh Do ..ClassCalls(NumCalls) Set End=$zh Write "Class calls: ",End-Start,! } ClassMethod ClassCalls(NumCalls As %Integer) { For i=1:1:NumCalls { Set x=..Compute(NumCalls) } } }
Class Community.perf.ClassAbs [ Abstract ] { ClassMethod Compute(Num As %Integer) { ;Quit Num Set ret=Num Quit ret } }
How about performance?
EPTEST>do ##class(Community.perf.ClassMain).Compare(100000000) Class calls: 31.675438
In latest version of IRIS (and Cachè?) inherited members method code is no longer duplicated, so there is no difference then using separate classes but I think this approach is more modern, elegant and, depending on situations, MUCH more flexible,