Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Builder of Attributes. Documentation improvements #382

Merged
merged 5 commits into from
Nov 25, 2023

Conversation

iRevive
Copy link
Contributor

@iRevive iRevive commented Nov 21, 2023

Closes #371.

@iRevive iRevive changed the title Add Builder of Attributes. Documentation improvements. Add Builder of Attributes. Documentation improvements Nov 21, 2023
@NthPortal
Copy link
Contributor

wasn't the purpose of the builder to be mutable? while this is perhaps a little more efficient than before, there's still a decent bit of copying

@iRevive
Copy link
Contributor Author

iRevive commented Nov 21, 2023

wasn't the purpose of the builder to be mutable? while this is perhaps a little more efficient than before, there's still a decent bit of copying

You are right, we should use a mutable builder instead.

@iRevive
Copy link
Contributor Author

iRevive commented Nov 21, 2023

I've added a mutable builder.

@iRevive iRevive force-pushed the attributes-refactor branch 4 times, most recently from 7f23bec to 6814e88 Compare November 21, 2023 11:40
@NthPortal
Copy link
Contributor

NthPortal commented Nov 21, 2023

what if we made Attributes extend immutable.Iterable[Attribute[_]]? (happy to push this up to another PR if desired)

@iRevive
Copy link
Contributor Author

iRevive commented Nov 21, 2023

what if we made Attributes extend immutable.Iterable[Attribute[_]]?

interesting idea. Then we can drop the custom foldLeft, forall, etc.

@iRevive
Copy link
Contributor Author

iRevive commented Nov 21, 2023

@NthPortal thanks for the idea! I like the outcome


def iterator: Iterator[Attribute[_]] = m.values.iterator

override def toList: List[Attribute[_]] = m.values.toList
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compilation fails without this override in Scala 3:

[error] 177 |  private final class MapAttributes(
[error]     |                      ^
[error]     |class MapAttributes needs to be abstract, since def toList: List[org.typelevel.otel4s.Attribute[?]] in trait Attributes in package org.typelevel.otel4s.sdk is not defined 
[error] one error found

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... what?? that makes no sense.

the only thing I can think of is that Iterable already defines toList, but that doesn't really explain things

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... what?? that makes no sense.

the only thing I can think of is that Iterable already defines toList, but that doesn't really explain things

I didn't find a meaningful answer either. ¯\_(ツ)_/¯

I assume Scala 3 compiler handles some inheritance cases differently.


/** Returns the `Map` representation of the attributes collection.
*/
def toMap: Map[AttributeKey[_], Attribute[_]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm torn between this being toMap vs asMap. asMap has the implication that Attributes is already either like a map (and can be easily wrapped as one) or backed by a map. is that something that we should commit to publicly as its design?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. Collections use to{X}. Technically, Attributes is a collection now.
Across the code base, we only use asMap in TraceState.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collections use to{X}

except for conversions (i.e. wrappers) between Java and Scala collections, which use as{Java,Scala}

@NthPortal
Copy link
Contributor

NthPortal commented Nov 21, 2023

@iRevive I wrote up my own implementation of Attributes as a collection (see #384) which is a bit more complete and has more overrides, as I have lot of experience with the collections framework. however, you added a bunch of docs, which mine does not have. feel free to steal my work and just use it here if you want

@iRevive
Copy link
Contributor Author

iRevive commented Nov 22, 2023

I've backported some changes from #384.

And a few benchmarks:

Benchmark                                    Mode  Cnt      Score    Error   Units
AttributesBenchmark.java_ofOne               avgt   15      4.031 ±  0.008   ns/op
AttributesBenchmark.java_ofFive              avgt   15    134.074 ±  0.086   ns/op
AttributesBenchmark.java_builderTenItem      avgt   15    486.882 ± 13.295   ns/op
AttributesBenchmark.java_computeHashCode     avgt   15     13.207 ±  0.027   ns/op
----------------------------------------------------------------------------------
AttributesBenchmark.otel4s_ofOne             avgt   15     26.083 ±  0.136   ns/op
AttributesBenchmark.otel4s_ofFive            avgt   15    456.209 ±  4.714   ns/op
AttributesBenchmark.otel4s_builderTenItem    avgt   15    792.389 ±  1.734   ns/op
AttributesBenchmark.otel4s_computeHashCode   avgt   15   3018.399 ± 16.982   ns/op

Some overhead is expected because we use Attribute(key, value) under the hood. Java SDK stores keys and values directly in the array.

Benchmarks with GC
Benchmark                                                      Mode  Cnt      Score    Error   Units
AttributesBenchmark.java_builderTenItem                        avgt   15    486.882 ± 13.295   ns/op
AttributesBenchmark.java_builderTenItem:gc.alloc.rate          avgt   15   1081.645 ± 27.242  MB/sec
AttributesBenchmark.java_builderTenItem:gc.alloc.rate.norm     avgt   15    552.001 ±  0.001    B/op
AttributesBenchmark.java_builderTenItem:gc.count               avgt   15     55.000           counts
AttributesBenchmark.java_builderTenItem:gc.time                avgt   15     30.000               ms

AttributesBenchmark.java_computeHashCode                       avgt   15     13.207 ±  0.027   ns/op
AttributesBenchmark.java_computeHashCode:gc.alloc.rate         avgt   15      0.001 ±  0.003  MB/sec
AttributesBenchmark.java_computeHashCode:gc.alloc.rate.norm    avgt   15     ≈ 10⁻⁵             B/op
AttributesBenchmark.java_computeHashCode:gc.count              avgt   15        ≈ 0           counts

AttributesBenchmark.java_ofFive                                avgt   15    134.074 ±  0.086   ns/op
AttributesBenchmark.java_ofFive:gc.alloc.rate                  avgt   15    967.157 ±  0.611  MB/sec
AttributesBenchmark.java_ofFive:gc.alloc.rate.norm             avgt   15    136.000 ±  0.001    B/op
AttributesBenchmark.java_ofFive:gc.count                       avgt   15     48.000           counts
AttributesBenchmark.java_ofFive:gc.time                        avgt   15     25.000               ms

AttributesBenchmark.java_ofOne                                 avgt   15      4.031 ±  0.008   ns/op
AttributesBenchmark.java_ofOne:gc.alloc.rate                   avgt   15  11354.241 ± 23.128  MB/sec
AttributesBenchmark.java_ofOne:gc.alloc.rate.norm              avgt   15     48.000 ±  0.001    B/op
AttributesBenchmark.java_ofOne:gc.count                        avgt   15    319.000           counts
AttributesBenchmark.java_ofOne:gc.time                         avgt   15    171.000               ms

AttributesBenchmark.otel4s_builderTenItem                      avgt   15    792.389 ±  1.734   ns/op
AttributesBenchmark.otel4s_builderTenItem:gc.alloc.rate        avgt   15   3494.463 ±  7.684  MB/sec
AttributesBenchmark.otel4s_builderTenItem:gc.alloc.rate.norm   avgt   15   2904.001 ±  0.002    B/op
AttributesBenchmark.otel4s_builderTenItem:gc.count             avgt   15    175.000           counts
AttributesBenchmark.otel4s_builderTenItem:gc.time              avgt   15     85.000               ms

AttributesBenchmark.otel4s_computeHashCode                     avgt   15   3018.399 ± 16.982   ns/op
AttributesBenchmark.otel4s_computeHashCode:gc.alloc.rate       avgt   15   2325.055 ± 13.112  MB/sec
AttributesBenchmark.otel4s_computeHashCode:gc.alloc.rate.norm  avgt   15   7360.004 ±  0.009    B/op
AttributesBenchmark.otel4s_computeHashCode:gc.count            avgt   15    117.000           counts
AttributesBenchmark.otel4s_computeHashCode:gc.time             avgt   15     59.000               ms

AttributesBenchmark.otel4s_ofFive                              avgt   15    456.209 ±  4.714   ns/op
AttributesBenchmark.otel4s_ofFive:gc.alloc.rate                avgt   15   3394.497 ± 34.863  MB/sec
AttributesBenchmark.otel4s_ofFive:gc.alloc.rate.norm           avgt   15   1624.001 ±  0.001    B/op
AttributesBenchmark.otel4s_ofFive:gc.count                     avgt   15    170.000           counts
AttributesBenchmark.otel4s_ofFive:gc.time                      avgt   15     87.000               ms

AttributesBenchmark.otel4s_ofOne                               avgt   15     26.083 ±  0.136   ns/op
AttributesBenchmark.otel4s_ofOne:gc.alloc.rate                 avgt   15   5264.220 ± 27.369  MB/sec
AttributesBenchmark.otel4s_ofOne:gc.alloc.rate.norm            avgt   15    144.000 ±  0.001    B/op
AttributesBenchmark.otel4s_ofOne:gc.count                      avgt   15    216.000           counts
AttributesBenchmark.otel4s_ofOne:gc.time                       avgt   15    108.000               ms


@NthPortal
Copy link
Contributor

NthPortal commented Nov 22, 2023

Benchmark                                    Mode  Cnt      Score    Error   Units
AttributesBenchmark.java_computeHashCode     avgt   15     13.207 ±  0.027   ns/op

13ns?! is it just identity hash code in the Java implementation?

@iRevive
Copy link
Contributor Author

iRevive commented Nov 22, 2023

Benchmark                                    Mode  Cnt      Score    Error   Units
AttributesBenchmark.java_computeHashCode     avgt   15     13.207 ±  0.027   ns/op

13ns?! is it just identity hash code in the Java implementation?

The implementation is this one: https://github.com/open-telemetry/opentelemetry-java/blob/main/api/all/src/main/java/io/opentelemetry/api/common/ArrayBackedAttributes.java.

And the hash function is defined here https://github.com/open-telemetry/opentelemetry-java/blob/main/api/all/src/main/java/io/opentelemetry/api/internal/ImmutableKeyValuePairs.java#L250

@NthPortal
Copy link
Contributor

And the hash function is defined here

oh, it caches it in a field. that'll do it. is that worth doing for us? I'm guessing not

@iRevive
Copy link
Contributor Author

iRevive commented Nov 22, 2023

oh, it caches it in a field. that'll do it. is that worth doing for us? I'm guessing not

We can keep everything as is, I believe. And implement caching only if the current version will affect performance so badly (I doubt that's the case).

Comment on lines +103 to +127
private val values = List.tabulate(10)(i => s"value$i")

private val jKeys = List.tabulate(10)(i => JAttributeKey.stringKey(s"key$i"))
private val jAttributes = List.tabulate(10) { i =>
val size = i + 1
val pairs = jKeys.take(size).zip(values.take(size))

pairs
.foldLeft(JAttributes.builder()) { case (builder, (key, value)) =>
builder.put(key, value)
}
.build()
}

private val keys = List.tabulate(10)(i => AttributeKey.string(s"key$i"))
private val attributes = List.tabulate(10) { i =>
val size = i + 1
val pairs = keys.take(size).zip(values.take(size))

pairs
.foldLeft(Attributes.newBuilder) { case (builder, (key, value)) =>
builder.addOne(key, value)
}
.result()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's been so long since I've done any benchmarking, but I think we want these defined in the benchmark instance rather than the companion, and I think there's also a setup method where you're supposed to construct them to prevent some compiler optimisations that wouldn't happen in regular code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iRevive iRevive merged commit c768309 into typelevel:main Nov 25, 2023
10 checks passed
@iRevive iRevive deleted the attributes-refactor branch November 25, 2023 08:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Attributes builder
2 participants