There is a database performance page located at db-perf
Help to Determine Performance Issues (4.x)
You can use xprf and xprf_dialog to help identify performance problems. You can get an xprf results in several ways:
!x << xprf.new_when_send(exemplar,:method|()|)
#or
!x << xprf.new_from_call(my_proc,arg1,arg2,etc...)
#or
!x << xprf.new_from_send(exemplar,:method|()|,arg1,arg2,etc...)
xprf_dialog.open(!x)
- new_when_send will return xprf the next time the method is called.
- new_from_call runs the given proc
- new_from_send runs the given method on the exemplar.
If you get a stack_overflow traceback, increase the light_thread.vm_max_depth. Not sure the memory ramifications of increasing this. Example:
light_thread.vm_max_depth << 8192
Constant vs. is_kind_of?()
Do you need to check what an object is? For example a pole exemplar could be inheriting from structure. So you want to ask an exemplar "is it a structure?" There are several ways of doing that. There is a method called is_kind_of?() on the object exemplar. It is pretty fast. Here is an example:
_block
_local t << system.time_now()
_for a _over 1.upto(1000000)
_loop
ed_pole.is_kind_of?(ed_structure)
_endloop
write(system.time_now()-t)
_endblock
Result: 9 seconds
However if you need that little extra speed….. Try creating a shared constant on the structure exemplar. (you will need to put it on a more "core" exemplar so you do not get a does_not_understand error). Here is an example. A shared constant called is_structure? is created on the ed_structure exemplar and is called.
_block
_local t << system.time_now()
_for a _over 1.upto(10000000)
_loop
ed_pole.is_structure?
_endloop
write(system.time_now()-t)
_endblock
Result: 3 seconds
Summary
You will note the shared constant is much quicker (note the loop amount, 1 million for is_kind of vs 10 million for constant). But, it is really only quicker if you are running the test more then 1 million times. So if you are only calling the test occasionally, then it probably isn't worth having a constant on the exemplars.
is_kind_of? vs rwo_type
If you have a big complex object model, then the performance can be much worse than above. A few examples from PNI will demonstrate:
t1 << _proc () pole.is_kind_of?(mit_hub) _endproc
$
proc unnamed
MagikSF> t1.time(10000)
$
2.594
t1 << _proc () pole.is_kind_of?(pole) _endproc
$
proc unnamed
MagikSF> t1.time(10000)
$
0.01000
We see from the above examples that a fairly modest 10000 unsuccessful tests are taking 2.5 seconds, which is going to be a big hit on our application responsiveness. The second test demonstrates that with a successful test on the top level of the object hierarchy the test can be very cheap - this also shows that it really is the 'is_kind_of?()' test that is hitting performance rather than the performance test infrastructure.
For RWOs a good alternative to the 'is_kind_of?()' test is to use 'rwo_type'. Below we repeat the above tests using 'rwo_type'.
MagikSF> t1 << _proc () result << (v.collections[:pole].an_element().rwo_type = :pole) _endproc
$
proc unnamed
MagikSF> t1.time(10000)
$
0.1210
MagikSF> t1 << _proc () result << (v.collections[:pole].an_element().rwo_type = :mit_hub) _endproc
$
proc unnamed
MagikSF> t1.time(10000)
$
0.1410
Despite doing more in the test the test times are an order of magnitude less than using 'is_kind_of?()'.
NB. If using PNI then there is a variant called 'is_type_of?()' that can test against multiple target types - this uses 'rwo_type' underneath the hood.
is_kind_of? vs inherits_from?
Which is faster? The following code was run 1000 times, and the results were 0 and 15. When I ran it 10000, the results were 78 and 47. That's right the second test was longer for is_kind_of?. To confirm, ran 100000, and the results were 656 and 578. So what does this mean…. see summary.
_global !t1 <<
_proc(size)
_local b
_local c << 0
_local t << system.fcsi_time_now_mil()
_for a _over 1.upto(size)
_loop
b << area.is_kind_of?(top_level_geometry)
c+<< 1
_endloop
write(system.fcsi_time_now_mil()-t)
c << 0
t << system.fcsi_time_now_mil()
_for a _over 1.upto(size)
_loop
b << area.inherits_from?(top_level_geometry)
c+<< 1
_endloop
write(system.fcsi_time_now_mil()-t)
_endproc
Note from Roger Fretwell: 'is_kind_of?' performs worse the deeper the inheritance tree of an object, and also for RWOs a much faster alternative is to use 'rwo_type', or in PNI 'is_type_of?()'.
Summary
Not much difference at the 100000 times. but if you need that little extra… Use inherits_from?. Use is_kind_of? for less then 1000 iterations.
Sorted_collection vs. Equality_rope
Have you noticed that IDs can be large values (especially if 64 bit). THe large values are not a typical integer, but rather a 'bignum'. And bignums need to be compared with each other using an equality. So if you need to create a very large collection with these bignums, you need something that does equality checking. You have several options, but for comparison here, an equality_rope was first tested with 600000 entries. When tested for a value using includes?() on the equality_set, it took over 70 seconds. Because equality_rope uses an equality test for includes?(), it is nearly 10x slower then the regular rope doing identitiy tests. If you compare this to using a sorted_collection, you are nearly at the same speed as an identity test.
Summary
Use sorted collections over equality testing for IDs.
Equality Property List vs as_symbol()
So the question is should I create a equality property list with strings as the keys, or a regular property_list with symbols, and constantly convert a string to a symbol… Well here are the tests.
equality_prop_list_test.magik has 2 procs, t1 & t2. t1 consistently ran 50% slower. It appears the conversion to as_symbol() and then looking it up is slower than the builtin equality look up.
Summary
Use Equality Property List.
Database Notifications
This could be in the database section, but it is done thru magik, so I put it here. In 4.2 (maybe 4.1) Smallworld introduced the ability to turn off database notifications. This seems to help prevent the database update from triggering all the dependent plugins from running when you are doing bulk modifications. To use:
_dynamic !notify_database_data_changes?! << _false
# Your code here
# You need to trigger the changes yourself at the end
aView.notify_suppressed_data_changes()
There is another system in place if the above code doesn't help.
_protect
view.suppress_notification()
#... load all ...
<...>
_protection
view.restore_notification(_false) # use true if you want notifications to be sent at end.
_endprotect