JSR223 performance

Hi,

I am having trouble with poor performance of the xtend-engine.
How is the jsr223 engine performing compared to xtend?
Could anyone do a comparison (like write 1000 values in a hashmap in xtend an jython)?
This would be much appreciated.

It’s trivial to write a Jython program to do the test you describe. It could be a standalone Jython program if you don’t want to install the JSR223 binding.

I am aware of that but I am still struggling with the installation.
So I wanted to know if its worth the effort! :wink:
And I am having preformance issues with xtend and there fore I am curious if this might fix my issues.

FWIW, I ran that test (standalone program) on my workstation and it takes 1 millisecond to add 1000 values to a Python dictionary (Windows 7 64 bit, 3 GHz, 16 core, 64 GB RAM).Of course, performance will vary greatly depending on the computer where the code was run.

Thank you very much. Reading 5 values from an XTend Hashmap takes between 2 and 20ms on my machine (8 Core, 16GB, ). On the RaspberryPi this goes up to 150ms - 750ms. So I’ll definately continue to try jython.

I finally got everything running and did a test as described.
I ran the test multiple times to avoid first time slowness.
This is the result of the last run:

Xtend:

val tsstart = DateTimeUtils::currentTimeMillis()

var HashMap<String, String> map_soll =    newLinkedHashMap( "" -> "") as LinkedHashMap<String, String>

var i = 0

do {
    map_soll.put( String::format("%d", i), String::format("%d", i))
    i = i + 1
}
while( i < 100000)
logInfo(    "Wohnzimmer", String::format( "Dauer %dms", DateTimeUtils::currentTimeMillis() - tsstart))

Jython

    t = time.time()
    
    for i in range(1, 100000):
        bla["{:d}".format(i)] = "{:d}".format(i)
    
    self.logger.info("Wohnzimmer: {}ms", (time.time() - t) * 1000)

Output:

2016-03-18 15:54:44.030 [INFO ] [penhab.model.jsr223.Wohnzimmer] - Wohnzimmer: 217.99993515014648ms
2016-03-18 15:55:19.008 [INFO ] [penhab.model.script.Wohnzimmer] - Dauer 35197ms

Jython is ~160 times faster than xTend!!!

Just curious, but what home automation use case are you implementing where 2 to 20 ms makes a difference? Or is the intent to run on the Pi where I can absolutely see 750ms being a problem?

Have you tried different types of data structures instead of Hashmap in the Rules DSL? For example, an ArrayList, or move the state to Items, etc?

Would you mind posting your code?

Not trying to discourage you from moving to jython, just trying to learn. When we start work on a User’s/Rules Programmers Guide for OH 2 I would like to include at least a short section on this sort of performance issue. For example, is pulling a field from a Hashmap slower than storing the state in an Item and using gMyGroup.members.filter[i|i.name == “hashkey”].head.state?

Sure - I don’t mind showing my code. I created an extra thread for this.
My code is supposed to run on the pi2, so there is definitely a performance issue.
What makes the jython implementation even more elegant is that the dictionary are thread save so I don’t have to worry about locks and concurrency any more! :heart_eyes:

Are you sure you are actually measuring the Hashmap put and that the Rules DSL code isn’t being dominated by String::format? Per my response to your other posting, String::format, String +, and String.replace can be very expensive operations in Java (which is what the Rules DSL uses for those functions).

I’m reminded of a time when I saw a more than 70% speed up in some code I wrote by simply dropping using the java.util.Date object in favor of System.currentTimeMilliseconds. Sometimes it is the simple things that trip you up.

Might be. Even if I optimize it and it’ll run 400% faster, then it is still 40 times slower than what I saw with jython.
When I take the perspective of the person which writes the rule, then I don’t really care where the delay comes from. I just want it to always run as fast as possible.

I’m perfectly willing to believe that the jython will run faster. What I am not convinced of us that the tests that you have run thus far are actually a fair test. In other words you are comparing how fast the Rules DSL does String manipulation and logging (which I will tell you now will be slower than pretty much every other language out there) to jython instead of what your original intent was which is to compare the performance of Hashmap. This is like those Language X is slower than Language Y arguments which, when you look at the actual code they used unfairly handicaps Language Y. It is an unfair test and leads to an incorrect conclusion.

If you implemented my suggestions I would be surprised if the performance differences remain quite so large. A 400% to 500% speed up is not an unreasonable expectation.

When I take the perspective of the person which writes the rule, then I don’t really care where the delay comes from. I just want it to always run as fast as possible.

I don’t understand this statement at all.

If you don’t care where the delay comes from you will never know what the advantages and disadvantages of one language over another, how to write optimized code in your given language, nor how to get any code to “run as fast as possible.” Nor do you know whether the conclusions you draw from your test are accurate. You can’t write fast code unless you know why it is slow. And you can’t write fast code unless you understand how the underlying language works.

At this point I think all you have proven is that String manipulation is slower in the Rules DSL than it is in jython when you try to do String manipulation in a jython manner rather than the known better performing way to do it in Rules DSL. I could have told you that from the start. It’s a shame because you are basically accusing the Rules DSL of being slow because you can’t program it in like Python and have it run fast.

EDIT: I thought this was a response on the other thread and it came across as dismissing all my syuggestions and I got a little miffed. Edited to take the offense out.

Hey Rich,

no problem I am really appreciation your help and all your knowledge.
I have learned quite a lot from you and from our last discussion we had.
So no offense taken. We’ll talk about your suggestions and the (postive) effect they had in the other thread.

What I am trying to to is to compare two languages basted on what I think might be a common use case. This is - at least for me the - the usage of hashmaps. These definitely serve as a class replacement in the Rule Engine.
I am not a python expert ether, I have just started learning it.
So my impression was that the jsr223 with jython was running faster and I wanted to quantify how much faster it was and wether it is worth the effort to spend more time with it. There are some things which are definitely more elegant with the rule engine, timers and closures for example. But there are (imo) lots of other things which are quite elegant in python and therefor jython (the possibility to create real functions, classes and to create library functions which are callable from every file). I am aware that these are things that are not needed by most of the openhab users out there.
But when I was first starting I was very much struggling with the rule engine, coming from a C/C++ background. The transition to python was much easier for me.
So I just wanted to know for me, are there - expect the language differences - any performance differences whatsoever between the engines. That’s what I was trying to quantify.

Based upon your suggestion I did change the test to Integer and Float. It really makes a huge difference - you are right.

rule "test"
when
    Item test changed or
    System started
then
    logInfo(    "Wohnzimmer", String::format( "Int Test start!"))
    
    var HashMap<Integer, Integer> map_soll =    newLinkedHashMap()
    
    var i = 0
    
    var tsstart = DateTimeUtils::currentTimeMillis()
    do {
        map_soll.put( i, i)
        i = i + 1
    }
    while( i < 100000)

    logInfo(    "Wohnzimmer", String::format( "Int Test duration %dms", DateTimeUtils::currentTimeMillis() - tsstart))

    //Float test
    logInfo(    "Wohnzimmer", String::format( "Float Test start!"))
    
    var HashMap<Integer, Float> map_float =    newLinkedHashMap()
    val Float const_float = 1.23456789.floatValue()
    
    i = 0
    tsstart = DateTimeUtils::currentTimeMillis()
    do {
        map_float.put( i, const_float)
        i = i + 1
    }
    while( i < 100000)

    logInfo(    "Wohnzimmer", String::format( "Float Test duration %dms", DateTimeUtils::currentTimeMillis() - tsstart))
end

It runs over 75% faster without changing anything!

2016-03-19 20:12:00.233 [INFO ] [penhab.model.script.Wohnzimmer] - Int Test start!
2016-03-19 20:12:23.315 [INFO ] [penhab.model.script.Wohnzimmer] - Int Test duration 23080ms
2016-03-19 20:12:23.315 [INFO ] [penhab.model.script.Wohnzimmer] - Float Test start!
2016-03-19 20:12:46.366 [INFO ] [penhab.model.script.Wohnzimmer] - Float Test duration 23049ms
2016-03-19 20:12:57.347 [INFO ] [penhab.model.script.Wohnzimmer] - Int Test start!
2016-03-19 20:13:20.056 [INFO ] [penhab.model.script.Wohnzimmer] - Int Test duration 22705ms
2016-03-19 20:13:20.056 [INFO ] [penhab.model.script.Wohnzimmer] - Float Test start!
2016-03-19 20:13:42.874 [INFO ] [penhab.model.script.Wohnzimmer] - Float Test duration 22817ms

This new test is a much better test but it still doesn’t really give you a good measure of the true Hashmap performance. As you are seeing something as simple as changing the data type can have a huge impact on the performance. To measure only the performance of the Hashmap you don’t want to be doing any other work inside the while loop you are measuring. That includes the +.

That is what makes these sorts of tests really really tricky to write. As soon as you add more work into the loop like the addition then you are left with the question: “Is it the Hashmap that is slower or is it the addition?”

To truly measure the performance of the Hashmap and only the Hashmap what I would do is pre-calculate i and put it in an ArrayList then make your while loop pull i out of the ArrayList rather than calculating it in the while loop. You are still stuck measuring how long the ArrayList.get method takes but since the Rules DSL doesn’t have primitive arrays I see no easy way around that.

Which version of jython do you use for your test. I noticed a huge performance issue with version 2.7.1 and 2.7.2 so I rolled back to 2.7.0

I think it was 2.7.0 if I remember correctly.

However I’ve completely switched to HABApp so I have no performance issues whatsoever and lots of other nice things :wink: