clara

http://www.clara-rules.org/
eraserhd 2018-10-31T18:32:09.028500Z

Hi! So we're debugging a performance issue, and it seems like, in our example, fast-token-compare taskes 142 seconds of self time, while PriorityQueue.remove() takes 139 seconds of self time.

eraserhd 2018-10-31T18:33:41.029800Z

It's not clear from VisualVM's sampling, but it looks like this referring to the PriorityQueue.remove(Object) method, which is consistent with the fast-token-compare times.

eraserhd 2018-10-31T18:34:39.030800Z

This method is linear for priority queue. It should be possible to wrap PriorityQueue to make it log(n) by checking when values are extracted. Is there some reason this won't work?

eraserhd 2018-10-31T18:36:47.031300Z

Alternately, are we doing something wrong to get runtime dominated by PriorityQueue.remove(Object)?

2018-10-31T19:06:01.031900Z

@eraserhd > Alternately, are we doing something wrong to get runtime dominated by PriorityQueue.remove(Object)? Seems like there is a lot of thrash of rules getting activated, but then subsequently “deactivated”

2018-10-31T19:06:27.032400Z

> This method is linear for priority queue. It should be possible to wrap PriorityQueue to make it log(n) by checking when values are extracted. Is there some reason this won’t work? I don’t understand what this is and how it’d make it log(n).

2018-10-31T19:07:02.033100Z

and lastly, fast-token-compare may be slowing down due to large tokens in these rules that are struggling with if they are satisfied/not satisfied

2018-10-31T19:07:35.033900Z

perhaps from large data accumulated via something like (acc/all)’s or similar

👆 1
eraserhd 2018-10-31T19:08:48.035300Z

@mikerod if remove/1 stores objects in a hash set, then remove/0 and add/1 skip removed tokens, remove/1 becomes O(1) and I think remove/0 is still O(log N)

2018-10-31T19:09:08.035800Z

We mostly avoid hashing objects

eraserhd 2018-10-31T19:09:12.036Z

Dijkstra's algorithm is frequently implemented like this, because removing from the priority queue is expensive.

2018-10-31T19:09:35.036300Z

because then you hit perf barriers with slow hash code impl’s

2018-10-31T19:09:49.036600Z

and often that comes down to the facts being used in the session

2018-10-31T19:10:16.037100Z

in the particular case where the prio queue is used, the object to remove is a new instance too, so wouldn’t be able to have a reusable cached hashcode

eraserhd 2018-10-31T19:11:07.037400Z

Hmm.

2018-10-31T19:11:42.037900Z

not to say it isn’t worth consideration at any time, just saying hashing isn’t always as cheap as it seems

2018-10-31T19:12:03.038300Z

fast-token-compare avoids deep equals most of the time too

2018-10-31T19:12:19.038700Z

it uses identity-based equivalence as a first-pass, so via identical? in clj semantics

2018-10-31T19:12:30.039Z

and typically that is all that is needed to find things

2018-10-31T19:12:38.039300Z

during the fire-rules loop at least

2018-10-31T19:12:52.039800Z

I’ve seen this problem of large tokens being slow though

2018-10-31T19:13:29.040900Z

the activation stuff is what uses the prio queue, it has to do with how when rules are satisfied, they are “staged” as available to have their RHS action executed at some future time

2018-10-31T19:13:54.041600Z

it is a prio queue since you can have precedence on which rules should be staged ahead of one another - e.g. :salience

2018-10-31T19:16:06.043600Z

I think there are some related issues out there (though not explained the same) https://github.com/cerner/clara-rules/issues/385

2018-10-31T19:16:56.043900Z

well, that’s the only one I see at the moment

2018-10-31T19:17:14.044400Z

I know I’ve messed around with this sort of problem before though, not sure I ever had a good answer

2018-10-31T19:17:26.044800Z

sometimes you could just avoid a rule setup that is causing it though

eraserhd 2018-10-31T19:17:36.045300Z

Those stats look very much like ours.

2018-10-31T19:17:46.045600Z

your hashing thing could be attempted at least, just have to consider a lot of things if it ends up looking like a good path

2018-10-31T19:18:06.046Z

the issue above talks about a “distinct accumulator”, I think that may be overly specific

2018-10-31T19:18:10.046200Z

probably a more general problem

2018-10-31T19:18:15.046400Z

and I’m fairly sure you have big tokens involved

2018-10-31T19:18:47.047100Z

a big token would come from a rule left-hand side (LHS) that had a lot of facts associated with a single activation of the rule

2018-10-31T19:21:03.047600Z

Take for example something like

(r/defrule big-activation-maker
  [?xs <- (acc/all) :from [X]]
  =>
  (do-stuff ?xs))

2018-10-31T19:21:14.047900Z

now if you can get into a situation where the set of all X changes a lot

2018-10-31T19:21:26.048200Z

and there are a lot of Xs in working memory overall

2018-10-31T19:21:56.049Z

then this rule may keep activating/cancelling/re-activating, as the set of X grows - and each time there is a big token comparison to do these removals

2018-10-31T19:22:07.049300Z

in a rule as simple as above, it typically wouldn’t thrash though

2018-10-31T19:22:36.049900Z

Most of the time, the network’s “batched propagation” semantics would lead you to a situation where all the X are around in one go with only 1 rule activation resulting

2018-10-31T19:22:46.050300Z

but certain setups can cause thrashing when more conditions and things are involved

2018-10-31T19:23:04.050800Z

perhaps the distinct accumulator case was a situations like this one, I just don’t remember

eraserhd 2018-10-31T19:27:29.051300Z

This sounds like there is one thing we can write as a custom accumulator to avoid retriggering a whole slew of rules.

2018-10-31T19:27:50.051500Z

perhaps

ethanc 2018-10-31T19:28:26.052600Z

or play with salience, though as a last resort

2018-10-31T19:28:50.053100Z

yeah, sometimes that’s useful

2018-10-31T19:29:09.053500Z

sometimes you can look at the rules and sort of see where an accumulator rule may be getting retriggered in multiple phases of insert

2018-10-31T19:29:37.054100Z

and try to prevent that, there is salience to help, as well as just potentially logical structuring