You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Calling custom functions in a hot path (read: thousands of iterations) allocates a surprisingly large amount of memory for something that intuitively looks trivial / cheap to policy authors. I've added a benchmark test case in OPA previously, and while there are more factors at play here, the most obvious resource hog as far as memory utilization is concerned is the implementation of the bindings "array hashmap". Ironically, this implementation is itself an optimization, where an array is used for the first 16 items before switching over to a map. I have no reason to doubt that this may be an optimization in cases where many bindings need to be handled, but for a rule like the one in the example below — where only a single binding is actually used, this leaves us in a spot where each iteration allocates an array with room pre-allocated for 16 bindings, but only 1 is ever getting used!
refs contains value if {
walk(input, [_, value]) # thousands of itemsis_ref(value)
}
is_ref(value) if value.type =="ref"is_ref(value) if value[0].type =="ref"
Running BenchmarkCustomFunctionInHotPath with pprof's memory profiler enabled leaves no doubt as to where most of the cost is incurred.
In other words, we're allocating 650 megabytes for bindings where only 40 is needed / used. While this may be an extreme case, it's not a contrived one, and this was originally observed in Regal using quite real Rego :)
We should look into alternative implementations for bindings. Ideally one where we allocate exactly for what we need upfront (could the compiler tell us?) but if that's not possible, at least a much better ratio than our current one.
The text was updated successfully, but these errors were encountered:
Calling custom functions in a hot path (read: thousands of iterations) allocates a surprisingly large amount of memory for something that intuitively looks trivial / cheap to policy authors. I've added a benchmark test case in OPA previously, and while there are more factors at play here, the most obvious resource hog as far as memory utilization is concerned is the implementation of the bindings "array hashmap". Ironically, this implementation is itself an optimization, where an array is used for the first 16 items before switching over to a map. I have no reason to doubt that this may be an optimization in cases where many bindings need to be handled, but for a rule like the one in the example below — where only a single binding is actually used, this leaves us in a spot where each iteration allocates an array with room pre-allocated for 16 bindings, but only 1 is ever getting used!
Running
BenchmarkCustomFunctionInHotPath
with pprof's memory profiler enabled leaves no doubt as to where most of the cost is incurred.In other words, we're allocating 650 megabytes for bindings where only 40 is needed / used. While this may be an extreme case, it's not a contrived one, and this was originally observed in Regal using quite real Rego :)
We should look into alternative implementations for bindings. Ideally one where we allocate exactly for what we need upfront (could the compiler tell us?) but if that's not possible, at least a much better ratio than our current one.
The text was updated successfully, but these errors were encountered: