Description
Bound instruments is the idea of obtaining a reference "bound" to a particular set of attributes which is known ahead of time. By doing so, you can record measurements directly to the bound reference and avoid an internal lookup.
For example, suppose I know ahead of time that I need to record measurements to attribute sets {"color": "red", "shape", "square"}
, {"color": "red", "shape", "circle"}
, {"color": "blue", "shape", "square"}
, {"color": "blue", "shape", "circle"}
.
Without bound instruments, code to record these series might look like:
// Initialize counter and known attribute sets
LongCounter counter = meter.counterBuilder("my-counter").ofLongs().build();
Attributes redSquare = Attributes.builder().put("color", "red").put("shape", "square").build();
Attributes redCircle = Attributes.builder().put("color", "red").put("shape", "circle").build();
Attributes blueSquare = Attributes.builder().put("color", "blue").put("shape", "square").build();
Attributes blueCircle = Attributes.builder().put("color", "blue").put("shape", "circle").build();
// ... record measurements
counter.increment(1, redSquare);
counter.increment(1, redCircle);
counter.increment(1, blueSquare);
counter.increment(1, blueCircle);
Each time a measurement is recorded, the internals of the SDK must lookup the aggregated state corresponding to the attributes in a map.
With bound instruments, you can avoid this map lookup and improve the performance of record operations:
// Initialize counter and obtain bound references to known attribute sets
LongCounter counter = meter.counterBuilder("my-counter").ofLongs().build();
BoundLongCounter redSquare = counter.bind(Attributes.builder().put("color", "red").put("shape", "square").build());
BoundLongCounter redCircle = counter.bind(Attributes.builder().put("color", "red").put("shape", "circle").build());
BoundLongCounter blueSquare = counter.bind(Attributes.builder().put("color", "blue").put("shape", "square").build());
BoundLongCounter blueCircle = counter.bind(Attributes.builder().put("color", "blue").put("shape", "circle").build());
// ... record measurements
redSquare.increment(1);
redCircle.increment(1);
blueSquare.increment(1);
blueCircle.increment(1);
We should consider extending the metrics API to support this concept. I recently wrote a blog post comparing otel java metrics to other metrics systems in the java ecosystem (prometheus and micrometer), and one notable negative of otel was the lack of support for bound instruments. For java, avoiding the map lookup saves ~11 ns per record call on my machine.
Its worth noting that bound instruments serve a rather niche use case where: 1. The attribute sets are known ahead of time. 2. The application is very performance sensitive. If either of these is not true, then bound instruments are of limited value. (I previously argued that we should punt on support for bound instruments in the initial metrics API release for this very reason.) For example, some of the most popular metrics in otel are http.server.request.duration
and http.client.request.duration
, and neither of these would be able to benefit from bound instruments because the attribute values are computed at runtime using application context.