-
Notifications
You must be signed in to change notification settings - Fork 30
Description
Hey there,
I encountered some performance benchmarks of certain CEL expressions evaluations using the library and wanted to know if that's the expected outcome.
I saw https://github.com/cloud-custodian/cel-python/wiki/Early-Profiling-Data and https://github.com/cloud-custodian/cel-python/blob/main/benches/large_resource_set.py and wanted to validate performance time for large data sets.
The expression I tried running is the following -
((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:centos:centos:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:centos:centos:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:centos:centos:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:centos:centos:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:centos:centos:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:debian:debian_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:debian:debian_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:debian:debian:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:debian:debian:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:debian:debian_linux:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:fedoraproject:fedora:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:fedoraproject:fedora:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:fedoraproject:fedora:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:fedoraproject:fedora:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:fedoraproject:fedora:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:oracle:linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:oracle:linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:oracle:linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:oracle:linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:oracle:linux:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:redhat:enterprise_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:redhat:enterprise_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:redhat:enterprise_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:redhat:enterprise_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:redhat:enterprise_linux:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:novell:suse_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:novell:suse_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:novell:suse_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:novell:suse_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:novell:suse_linux:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:canonical:ubuntu_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:canonical:ubuntu_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:canonical:ubuntu_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:canonical:ubuntu_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:canonical:ubuntu_linux:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/h:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/h:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Windows Server" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:microsoft:")))) ? "Windows Team" : (((!has(class_a.property_a) ? false : ("Windows Server" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/h:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/h:")))) ? "Windows Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) || (!has(class_a.property_a) ? false : ("Windows Server" == class_a.property_a))) ? "Unassigned" : (((!has(class_a.property_a) ? false : ("Windows Workstation" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:microsoft:")))) ? "Solutions Team" : ((!has(class_a.property_a) ? false : ("Windows Workstation" == class_a.property_a)) ? "End User" : "unknown"))))))))))))
with the following context -
cel_context = {
"class_a": celpy.json_to_cel({"property_a":"something"}),
"class_b": celpy.json_to_cel({"title":"something else","property_b":"some var","integration_info":{"type":"GitHub"}}),
"optional": celpy.json_to_cel({})
}
The average I'm getting on my local machine (Macbook pro M1 Max, 32G RAM), is around 3.5 seconds per iteration - is this expected?
As a comparison using https://github.com/google/cel-java takes 1 second for the same 10k iterations which is a exponentially faster.
Is it possible to reach the java times? perhaps Lark in the evaluation is causing an issue here?
I tried also passing to the env the CompiledRunner class to maybe boost performance, but seems like its calling super() for the evaluation which raises NotImplementedError - so not sure how to evaluate the expressions as pure python.
I tried smaller scripts - this one takes 0.2 seconds per iteration -
(!has(class_b.integration_info.type) ?
false:("some value" == class_b.integration_info.type)) ?
optional.of("some value") : optional.of("some other value")
Which is still significantly slower then the java.
(need to run it with these custom functions -
functions = {
"of": lambda optional, value: value,
"none": lambda optional, : None
}
)
I know there is redundancy in the big expression and that it can be optimized, but I'm using it intentionally since it is user-generated and can be encountered in my system.
I'm adding here a python script I used for the benchmarking - tried to wrap the processing code with cProfile but it just slowed things down even more so this is based on using the time.
Any help on this would be much appreciated,
Thanks,
import celpy
import celpy.celtypes
import time
CEL_EXPRESSION_ORIGINAL = """
(
(
!has(class_a.property_a) ?
false : ("Linux" == class_a.property_a)
) &&
(
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/o:centos:centos:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/o:centos:centos:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/a:centos:centos:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/a:centos:centos:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("p-os:/a:centos:centos:")
)
)
) ?
optional.of("Linux Team") :
(
(
(
!has(class_a.property_a) ?
false : ("Linux" == class_a.property_a)
) &&
(
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/o:debian:debian_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/o:debian:debian_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/a:debian:debian:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/a:debian:debian:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("p-os:/a:debian:debian_linux:")
)
)
) ?
optional.of("Linux Team") :
(
(
(
!has(class_a.property_a) ?
false : ("Linux" == class_a.property_a)
) &&
(
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/o:fedoraproject:fedora:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/o:fedoraproject:fedora:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/a:fedoraproject:fedora:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/a:fedoraproject:fedora:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("p-os:/a:fedoraproject:fedora:")
)
)
) ?
optional.of("Linux Team") :
(
(
(
!has(class_a.property_a) ?
false : ("Linux" == class_a.property_a)
) &&
(
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/o:oracle:linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/o:oracle:linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/a:oracle:linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/a:oracle:linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("p-os:/a:oracle:linux:")
)
)
) ?
optional.of("Linux Team") :
(
(
(
!has(class_a.property_a) ?
false : ("Linux" == class_a.property_a)
) &&
(
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/o:redhat:enterprise_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/o:redhat:enterprise_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/a:redhat:enterprise_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/a:redhat:enterprise_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("p-os:/a:redhat:enterprise_linux:")
)
)
) ?
optional.of("Linux Team") :
(
(
(
!has(class_a.property_a) ?
false : ("Linux" == class_a.property_a)
) && (
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/o:novell:suse_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/o:novell:suse_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/a:novell:suse_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/a:novell:suse_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("p-os:/a:novell:suse_linux:")
)
)
) ?
optional.of("Linux Team") :
(
(
(
!has(class_a.property_a) ?
false : ("Linux" == class_a.property_a)
) &&
(
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/o:canonical:ubuntu_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/o:canonical:ubuntu_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/a:canonical:ubuntu_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/a:canonical:ubuntu_linux:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("p-os:/a:canonical:ubuntu_linux:")
)
)
) ?
optional.of("Linux Team") :
(
(
(
!has(class_a.property_a) ?
false : ("Linux" == class_a.property_a)
) &&
(
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/h:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/h:")
)
)
) ?
optional.of("Linux Team") :
(
(
(
!has(class_a.property_a) ?
false : ("Windows Server" == class_a.property_a)
) &&
(
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/o:microsoft:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/o:microsoft:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/a:microsoft:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/a:microsoft:")
)
)
) ?
optional.of("Windows Team") :
(
(
(
!has(class_a.property_a) ?
false : ("Windows Server" == class_a.property_a)
) &&
(
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/h:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/h:")
)
)
) ?
optional.of("Windows Team") :
(
(
(
!has(class_a.property_a) ?
false : ("Linux" == class_a.property_a)
) ||
(
!has(class_a.property_a) ?
false : ("Windows Server" == class_a.property_a)
)
) ?
optional.of("Unassigned") :
(
(
(
!has(class_a.property_a) ?
false : ("Windows Workstation" == class_a.property_a)
) &&
(
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/o:microsoft:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/o:microsoft:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("os:/a:microsoft:")
) ||
(
!has(class_b.property_b) ?
false : class_b.property_b.contains("x-os:/a:microsoft:")
)
)
) ?
optional.of("Solutions Team") :
(
(
!has(class_a.property_a) ?
false : ("Windows Workstation" == class_a.property_a)
) ?
optional.of("End User") : optional.of("Unknown")))))))))))))
"""
CEL_EXPRESSION_SHORT = """
"bla bla"
"""
CEL_EXPRESSION_MEDIUM = """
(!has(class_b.integration_info.type) ?
false:("some value" == class_b.integration_info.type)) ?
optional.of("some value") : optional.of("some other value")
"""
CEL_EXPRESSION_ORIGINAL_NO_OPTIONAL = """
((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:centos:centos:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:centos:centos:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:centos:centos:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:centos:centos:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:centos:centos:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:debian:debian_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:debian:debian_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:debian:debian:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:debian:debian:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:debian:debian_linux:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:fedoraproject:fedora:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:fedoraproject:fedora:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:fedoraproject:fedora:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:fedoraproject:fedora:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:fedoraproject:fedora:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:oracle:linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:oracle:linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:oracle:linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:oracle:linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:oracle:linux:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:redhat:enterprise_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:redhat:enterprise_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:redhat:enterprise_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:redhat:enterprise_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:redhat:enterprise_linux:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:novell:suse_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:novell:suse_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:novell:suse_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:novell:suse_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:novell:suse_linux:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:canonical:ubuntu_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:canonical:ubuntu_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:canonical:ubuntu_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:canonical:ubuntu_linux:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("p-os:/a:canonical:ubuntu_linux:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/h:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/h:")))) ? "Linux Team" : (((!has(class_a.property_a) ? false : ("Windows Server" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:microsoft:")))) ? "Windows Team" : (((!has(class_a.property_a) ? false : ("Windows Server" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/h:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/h:")))) ? "Windows Team" : (((!has(class_a.property_a) ? false : ("Linux" == class_a.property_a)) || (!has(class_a.property_a) ? false : ("Windows Server" == class_a.property_a))) ? "Unassigned" : (((!has(class_a.property_a) ? false : ("Windows Workstation" == class_a.property_a)) && ((!has(class_b.property_b) ? false : class_b.property_b.contains("os:/o:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/o:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("os:/a:microsoft:")) || (!has(class_b.property_b) ? false : class_b.property_b.contains("x-os:/a:microsoft:")))) ? "Solutions Team" : ((!has(class_a.property_a) ? false : ("Windows Workstation" == class_a.property_a)) ? "End User" : "unknown"))))))))))))
"""
functions = {
"of": lambda optional, value: value,
"none": lambda optional, : None
}
env = celpy.Environment()
before_compile = time.time()
ast = env.compile(CEL_EXPRESSION_ORIGINAL_NO_OPTIONAL)
print(f'after compile: {time.time() - before_compile}')
before_runner = time.time()
program = env.program(ast,functions=functions)
print(f'after runner: {time.time() - before_runner}')
before_eval = time.time()
def process():
for i in range(0, 100):
before_json_to_cel = time.time()
cel_context = {
"class_a": celpy.json_to_cel({"property_a":"something"}),
"class_b": celpy.json_to_cel({"title":"something else","property_b":"some var","integration_info":{"type":"GitHub"}}),
"optional": celpy.json_to_cel({})
}
after_row_pre_process = time.time() - before_json_to_cel
before_row_eval = time.time()
result = program.evaluate(cel_context)
after_row_eval = time.time()
print(f"""after eval row {i},
row time: {(after_row_eval - before_json_to_cel):.3f},
pre-process time: {after_row_pre_process:.3f},
eval time: {(after_row_eval - before_row_eval):.3f},
current total time: {((after_row_eval - before_eval)/60):.3f} minutes,
result: {result}""")
# pr = cProfile.Profile()
# pr.enable()
# print("started profiler")
process()
# print("disabling profiler")
# pr.disable()
# s = io.StringIO()
# ps = pstats.Stats(pr, stream=s).sort_stats(pstats.SortKey.TIME)
# ps.print_stats()
# print(s.getvalue())