Skip to content

Performance: criticality of code paths

Tommy Ludwig edited this page Aug 23, 2021 · 3 revisions
Note
This wiki is currently a Work-In-Progress.

Performance of Micrometer code is an important concern, but not all code paths have the same criticality to runtime performance. In addition to the time to execute code paths, we must also be conscious of garbage generated.

Highly critical paths

Code paths that are executed in line with user code directly add to the execution time of user code. Performance of these code paths are of highest criticality, especially if they may be used in short and often executed parts of user code.

In these highly critical paths, sacrifices to code readability may be made where significant performance benefits can be demonstrated.

Recording a measurement

This is the most critical path because it is expected to be called most often in line with user code.

The code for recording largely depends on the meter implementation, but there is a lot of use of classes from the java.util.concurrent.atomic package. Look, for example, at the implementation for the increment method for a Counter or the record method for a Timer/DistributionSummary.

Retrieving a meter

In many cases, a meter will be retrieved each time to record a measurement. In some cases, a reference to a meter may be retained if there are no dynamic tag values, and this can save the cost of retrieving the meter before each recording.

The Builder API for meters will often be used as a way to retrieve meters. The builder does come with the cost of allocation. For example:

JettySslHandshakeMetrics retrieving meter via Builder
@Override
public void handshakeSucceeded(Event event) {
    SSLSession session = event.getSSLEngine().getSession();
    Counter.builder(METER_NAME)
            .baseUnit(BaseUnits.EVENTS)
            .description(DESCRIPTION)
            .tag(TAG_RESULT, "succeeded") // constant tag
            .tag(TAG_PROTOCOL, session.getProtocol()) // dynamic tag
            .tag(TAG_CIPHER_SUITE, session.getCipherSuite()) // dynamic tag
            .tags(tags)
            .register(registry) // create or retrieve
            .increment(); // record
}

Any tags on the meter will involve the Tag and Tags API.

For the retrieval inside MeterRegistry code, besides getting the meter from the Map that holds meters, all MeterFilter are applied to the Meter or Id parameter to get the mapped ID which is the key for stored meters.

Less critical paths

Code paths that are executed separately from user code do not directly contribute to execution time. They do contribute indirectly since they are part of the same process. In a resource constrained environment, these code paths can contribute to user code execution time as much as the in line code paths.

Creating a meter

While each unique meter will need to be created once during the lifetime of the application and often in line with user code, it is considered less critical than retrieving a meter or recording a measurement. This is because creation only happens once per meter, whereas retrieval and/or recording is expected to happen many times per meter over the lifetime of the application. Additionally, unbounded cardinality is an anti-pattern for metrics, so meters created should be far fewer than measurement recording or meter retrieval.

See this code block in MeterRegistry for meter creation logic.

  • An object lock is used to synchronize meter creation.

  • The accept method of each MeterFilter is called on the meter ID to be created. If not accepted, a no-op meter is created instead.

  • If a meter takes a DistributionStatisticConfig, the configure method for all MeterFilter will be called on the config.

  • Synthetic associations are marked.

  • All meterAddedListeners are called on the meter before it is put into the meter Map.

Publishing metrics

Publishing happens separately from user code execution usually in a dedicated single thread pool, which makes it less critical that it has the absolutely highest performance possible. Publishing includes the following code paths:

  • Some registries use MeterPartition to partition meters into batches for publishing.

  • Retrieving all meters via MeterRegistry#getMeters which copies a List of the meters.

  • All NamingConvention API for sanitization and conventionalization of names/values from canonical form.

  • How meters are converted from Micrometer’s in-memory model to a publish format differs by implementation.

  • Depending on registry implementation, sending metric data may use the HttpSender API or a third-party API.

Release notes

1.14
1.13
1.12
1.11
1.10
1.9
1.8
1.7
1.6
1.5
1.4 (non-LTS)
1.3
1.2 (non-LTS)
1.1

Clone this wiki locally