Optimize getPropertyOrNull(PropertyDescriptor) access with index-based storage
- Dominant language
- Java
- Stars
- 9.5k
- Forks
- 2.3k
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 5
Description
### Use case
JMeter often queries properties at runtime. For instance, `ResultCollector` `isSuccessOnlyLogging` and `isErrorLogging` call `getProperty(String)` which is cotly.
### Possible solution
If test element schemas are specified with inheritance, then we could copy properties to an array where elements are `JMeterProperty` in their declaration order.
Then access `getPropertyOrNull(PropertyDescriptor)` could be optimized with indices.
For instance:
```kotlin
public open class TestElementSchema protected constructor() : DefaultTestElementSchema() {
public companion object INSTANCE : TestElementSchema()
public val name: StringPropertyDescriptor =
string("TestElement.name")
public val comments: StringPropertyDescriptor =
string("TestElement.comments")
...
```
```kotlin
public open class ResultCollectorSchema: TestElementSchema() {
public companion object INSTANCE: ResultCollectorSchema()
public val successOnly: BooleanPropertyDescriptor =
boolean("ResultCollector.success_only_logging")
public val errorOnly: BooleanPropertyDescriptor =
boolean("ResultCollector.error_logging")
```
It would result in the following indices:
```
0 name
1 comments
2 successOnly
3 errorOnly
```
Then, if we need to query `successOnly` at runtime, we can check if the element declares `ResultCollectorSchema` (or its subtype), and then we can access element `2` from properties array.
Here's a sketch:
```java
public class ResultCollector extends ... {
public boolean isSuccessOnlyLogging() {
return get(getSchema().getSuccessOnly());
// return getPropertyAsBoolean(SUCCESS_ONLY_LOGGING,false);
}
public boolean isErrorLogging() {
return get(getSchema().getErrorOnly());
// return getPropertyAsBoolean(ERROR_LOGGING);
}
```
```java
public abstract class AbstractTestElement ... {
private final transient JMeterProperty[] fastProps = new JMeterProperty[getSchema().getProperties().size()];
// Optimized property access
public JMeterProperty getPropertyOrNull(PropertyDescriptor key) {
int index = key.getIndex();
JMeterProperty[] fastProps = this.fastProps;
if (index >= 0 && index < fastProps.length) {
if (key.getSchema().isInstance(getSchema())) {
return fastProps[index];
}
}
return getPropertyOrNull(key.getName());
}
// Every property store would store the property to fastProps array
private void storeFastProperty(JMeterProperty prop) {
PropertyDescriptor descr = getSchema().getProperties().get(prop.getName());
if (descr != null) {
int propIndex = descr.getIndex();
if (propIndex >= 0 && propIndex < fastProps.length) {
fastProps[propIndex] = prop;
}
}
}
```
I assume we have only a few "property store" calls at the runtime, so it should be fine to perform extra work in `setProperty`, `remove(JMeterProperty)`.
A draft benchmark (see `ResultCollectorBenchmark` in `core/src/jmh`) shows ~8ns for the current `isSampleWanted` and ~3ns with the optimized `getPropertyOrNull(PropertyDescriptor` case.
It is not dramatic, however, it would be interesting to test it more.
### Possible workarounds
_No response_
### JMeter Version
5.5
### Java Version
_No response_
### OS Version
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.