[SOLVED] Floating point operations in rules causes severe performance issues on hard float capable hardware/software

Yes it should but you should double check it’s the right JVM installed.

But I second @vzorglub, it’s likely to be the primitives.

I see, I assumed I had to use DecinalType since the xtend docummentation says “There are only decimal floating-point literals.”. I guess I don’t understand that sentence fully :slight_smile:
Thank you for the code suggestion, running it however gives me this error.

2019-01-10 19:36:35.327 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'air quality changed': An error occurred during the script execution: Could not invoke method: java.lang.Math.abs(int) on instance: null

I checked newAqi which is the only thing not declared in the rule and it definately has a state.

aqiHome (Type=GroupItem, BaseType=NumberItem, Members=1, State=13, Label=aqiHome MAX, Category=null)

However the Category is null, but I have no idea what a Category is for an Item.
Any ideas why I get this error?

I thought thats what the hf stands for, or does the mixed mode thing mean it might not use it?

You don’t seem to be feeding that an int, though? Maybe it defaults to integer version of abs if fed null.

Investigate with logInfo

val newAqi = aqiHome.state as Number
   logInfo("test", "values " + newAqi.toString + " " + oldAqi.toString)
    if (Math::abs(newAqi-oldAqi) >= Xtrigger) {

This

    logInfo(logID, "aqiHome: "+ aqiHome.state.toString)                                      │To interact with openHAB on the command line, execute: 'openhab-cli --help'
    val newAqi = aqiHome.state as Number                                                     │
    logInfo(logID, "oldAqi: "+ oldAqi.toString)                                              │[19:10:48] openhabian@Marvin:/etc/openhab2/items$ cd..
    logInfo(logID, "newAqi: "+ newAqi.toString)

outputs this

2019-01-10 20:18:50.723 [INFO ] [se.smarthome.model.script.math.rules] - aqiHome: 7
2019-01-10 20:18:50.728 [INFO ] [se.smarthome.model.script.math.rules] - oldAqi: -100
2019-01-10 20:18:50.733 [INFO ] [se.smarthome.model.script.math.rules] - newAqi: 7

Edit: Sorry about strange post, apparently found some shortcut to click post :slight_smile:

Might help


but not eit drives you to primitives again for abs().

It might be more effecient to write your own if-then abs comparison for Numbers

Just realized it’s 7–100, can it be that the negative of a negative is undefined?

That’s fine. Isn’t that what your abs() is supposed to deal with?

I was thinking if it got confused by the double negative, but I just tested with other number and it does not affect it.

Could avoid abs() by rewrite as

    if ( ( (newAqi-oldAqi) >= 0 && >= Xtrigger) ||     // postive result
          ( 0 - (newAqi-oldAqi)) >= Xtrigger )  {         // negative result

I think my logic is correct … please check, causes headaches :crazy_face:

2 Likes

I have something working fairly fast now.
Using:

setPoint = Math::round(y.floatValue())

to round and I’ll write my own abs with if

1 Like

very nice, thank you! :smile:

Edit:
It threw and error but with this small edit it works nicely :slight_smile:

if ( ( (newAqi-oldAqi) >= 0 && (newAqi-oldAqi) >= Xtrigger) ||     // postive result         │
          ( 0 - (newAqi-oldAqi)) >= Xtrigger )  {                                 // negative result

As this seems a pretty severe example manifestation of performance issue, I’ve referenced this post in an existing issue

1 Like

Brilliant, right logic wrong brackets :crazy_face:

1 Like

Thank you :slight_smile:

Back in the thread
I thought that the abs function might cause a problem but I wasn’t sure
Is the rule faster now?

hf is the right one

It’s a known problem with no known solution in site. For some reason (I have unsubstantiated theories) the Rules parser has a really hard time parsing .rules files that use primitives.

Luckily you almost never have to use primitives.

The problem isn’t in actually performing the operation. The problem is with the parser. My theory of what is going on is that Xtend, as a weakly typed language, prefers to put off until run time type checking. But when you use primitives it cannot put off the type checking so it has to do a whole lot more work to verify that all the types check out at parse time.

By default without forcing it, Xtend will use the BigDecimal class to represent all Numbers. By forcing your variables to be primitives you are bypassing this.

So, there are some places where a primitive must be used. These are usually in calls to library methods like abs. You can use a primitive in just these places though. For example:

Math::abs((newAqi-oldAqi).intValue)

For one or two calls to a library like this I see no reason to avoid the calls to intValue or doubleValue where needed. That shouldn’t impact performance too much. Typically there will be only one or two such calls in a given Rule anyway.

1 Like

Many thanks for your help everyone!
Now I know how to avoid this in the future :smile:

Yes, of biblical proportions!
In the current version with all the suggestions from this thread it runs in about 30-60 ms, quite the improvement from the minutes before. So based on my very rough timing, 2500-5000 times faster ^^
Thank you for suggesting Numbers!

I use this with floatValue, it introduces no noticable delay and results in quite clean code. Thank you!

setPoint = Math::round(<my decimal value>.floatValue)
This is kind of the key for me, I need to calculate with decimals but have an integer number to send to my device. I thought using floats everywhere was the way to go, but obviously not :slight_smile:

Here is the current code, for reference.
Notice! This is not a finished rule, it’s simply the state in which it no longer have performance issues.

val logID = "math.rules"
val Number Y2 = 17
val Number Y1 = 1
val Number X2 = 100
val Number X1 = 0
var Number Xtrigger = 3.4
var Number oldAqi = -100
var Number setPoint = 300
// --------------- air quality changed ---------------
rule "air quality changed"
when
    // Item aqiHome changed
    Item Test_switch_4 received command ON
then
    val newAqi = aqiHome.state as Number
   if (Math::abs((newAqi-oldAqi).floatValue) >= Xtrigger) {
        logInfo(logID, "aqi changed more than: " + Xtrigger)
        setPoint = Math::round((((Y2-Y1)/(X2-X1)) * newAqi + Y2 - ((Y2-Y1)/(X2-X1)) * X2).floatValue)
        // purifier_1_favoritelevel.sendCommand(setPoint)
        logInfo(logID, "purifier setPoint: " + setPoint.toString)
    }
end

Log

2019-01-11 13:00:56.245 [vent.ChannelTriggeredEvent] - mihome:sensor_switch:7c49ebb3ac4c:158d0002c5e942:button triggered SHORT_PRESSED
2019-01-11 13:00:56.259 [ome.event.ItemCommandEvent] - Item 'Test_switch_4' received command ON

==> /var/log/openhab2/openhab.log <==
2019-01-11 13:00:56.273 [INFO ] [se.smarthome.model.script.math.rules] - aqi changed more than: 3.4
2019-01-11 13:00:56.290 [INFO ] [se.smarthome.model.script.math.rules] - purifier setPoint: 1

==> /var/log/openhab2/events.log <==
2019-01-11 13:01:07.928 [vent.ChannelTriggeredEvent] - mihome:sensor_switch:7c49ebb3ac4c:158d0002c5e942:button triggered SHORT_PRESSED
2019-01-11 13:01:07.950 [ome.event.ItemCommandEvent] - Item 'Test_switch_4' received command ON

==> /var/log/openhab2/openhab.log <==
2019-01-11 13:01:07.978 [INFO ] [se.smarthome.model.script.math.rules] - aqi changed more than: 3.4
2019-01-11 13:01:08.005 [INFO ] [se.smarthome.model.script.math.rules] - purifier setPoint: 1
2 Likes

Brilliant, please tick the solution post, thanks