Object Mapping Performance¶
Bson deserialization is a key factor to measure performance of KMongo, as usually you retrieve more data from MongoDB than you send to the db.
The benchmark sources are available in github.
We take a Bson representation of this class:
data class FriendWithBuddies( val _id: ObjectId? = null, val name: String? = null, val address: String? = null, val coordinate: Coordinate? = null, val gender: Gender? = null, val buddies: List<FriendWithBuddies> = emptyList() )
And deserialize it using:
- org.bson.Document (driverFriend)
- Jackson mapping (jacksonFriend)
- Native/POJO mapping (nativeFriend)
- kotlinx.Serialization (serializationFriend)
Measured with jmh:
As you can see, the org.bson.Document mapping (basically a Map) outperforms Jackson & Native mapping but kotlinx.Serialization is the best - this is expected: no reflection is involved for kotlinx.Serialization.
Note: during this benchmark, we have found a ~15% perf improvement for jackson but the fix is not included in the last released version of bson4jackson. However, even with this improvement, the ranking stays the same.
Is it possible to improve Jackson & native mapping performance? Of course! You can write a custom Jackson deserializer
or a custom codec for the
FriendWithBuddies class. Then you avoid reflection in both case, and you speed up your deserialization.
Then we get these results:
We can see that jackson is better now, but it is "Native" mapping that really shines. However, writing a custom deserializer or codec is cumbersome and error prone. For Jackson, there is a small library that generates automatically the deserializers for the class annotated with @JacksonData. Here is the result:
The custom deserializer version performs better, but you get a 40% improvement "for free" with
Now we take the same Bson than before, but we deserialize it in a partial object: a friend without its buddies.
data class Friend( val _id: ObjectId? = null, val name: String? = null, val address: String? = null, val coordinate: Coordinate? = null, val gender: Gender? = null )
Here are the results:
kotlinx.Serialization outperforms everything. This is because kotlinx.Serialization mapping
(like native mapping that has also a signifiant boost) skips the not mapped bson data,
Bson4Jackson parse the bson entirely.
If performance is critical for your use case, use "kotlinx.Serialization" mapping. But "Native" and Jackson does not perform that bad, and are also valid choices for almost all projects.