-
Notifications
You must be signed in to change notification settings - Fork 1k
Performance: criticality of code paths
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.
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.
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
.
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:
@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.
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.
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 eachMeterFilter
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
, theconfigure
method for allMeterFilter
will be called on the config. -
Synthetic associations are marked.
-
All
meterAddedListeners
are called on the meter before it isput
into the meterMap
.
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 aList
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.