Posts mit dem Label delegation werden angezeigt. Alle Posts anzeigen
Posts mit dem Label delegation werden angezeigt. Alle Posts anzeigen

Donnerstag, 26. Dezember 2019

Smart property delegation with Kotlin

Every now and then I have a use case where I have a data holder object that is wrapped by some other object. With Kotlin's interface delegation, it's very easy to implement an interface by delegating to the implementing property. However, sometimes you don't have interfaces, so I realized that there's one thing missing from the Kotlin standard lib: Have a property delegating to another property. Or at least I wasn't able to find it and it's kind of difficult to google that. So here's the solution:



I'm using a property reference (you need the reflection dependency for that) for generic property (get and set) usage. This is kind of a lense, known from functional programming I guess.

Nice things can done with that, for example typesafe ui components can be generated like that:

Montag, 17. September 2018

Kind-of-structs on the JVM using Kotlin's delegated properties

Project Valhalla is on everyone's lips nowadays, but the problem is: It is for years now and there's no concrete schedule when we can expect value types to be part of the JVM.

In games, there is a desperate need for value types or at least control about the object layout. Why? Because one has to share memory with the native side. For example OpenGL lets you use a persistent mapped buffer - combined with multibuffering and your own synchronization gives you a blazing fast multithreading approach for your engine. But OpenGL doesn't want to read your Java object's headers, that's why you can't use regular serialzation mechanisms and instead you have to put your objects into a ByteBuffer float by float or int by int.

Using standard Java/JVM heap objects, one has to update all the objects and afterwards extract them to a ByteBuffer. This means two iterations. Better would be to have objects that use a ByteBuffer directly, in order to be able to skip the buffer extraction completely.

Now there's Kotlin with its delegated properties. All the basic examples show how to use a hash map instance as a backing storage for arbitrary properties of an object (https://kotlinlang.org/docs/reference/delegated-properties.html). This led me to the idea to use delegated properties to access a ByteBuffer object as a backing storage for objects and structures of objects - just like structs in C do it.

interface Struct { byteOffset: Int buffer: ByteBuffer } // some missing magic here for property registration and local offset calculation class FloatProperty(val localOffset) { inline operator fun getValue(thisRef: Struct, KProperty<*,*>): Float { thisRef.buffer.getFloat(thisRef.byteOffset+localOffset) } } class MyStruct: Struct { // .. missing magic here val myFloat by FloatProperty() val mySecondFloat by FloatProperty() } This will result a flat Layout for a MyStruct instance. This can be used directly by native APIs. So what I'm interested in is, how well does this perform compared to Vanilla Java approach? How much overhead is there for all those methods and delegate instances? I tested several different implementations that differ in convenience for the user and overall performance. One surprise for me was, that none of my implementations (neither ByteBuffer nor Unsafe backed) is as fast as vanilla Java. There are some benchmarks on the internet that tell a different story, for example this one. I can't really tell you how it was achieved. Just that I wasn't able to achieve similar results.



Benchmark                                                       
Mode  Cnt      Score     Error  Units
iterAndMutBufferDirect                                         
thrpt   12  90626,796 ± 303,407  ops/s
iterAndMutKotlinDelegatedPropertySlidingWindowBuffer           
thrpt   12  23695,594 ±  82,291  ops/s
iterAndMutKotlinDelegatedPropertyUnsafeSimpleSlidingWindowBuffer
thrpt   12  27906,315 ±  52,382  ops/s
iterAndMutKotlinDelegatedPropertyUnsafeSlidingWindowBuffer     
thrpt   12  25736,322 ± 904,017  ops/s
iterAndMutKotlinSimpleSlidingWindowBuffer                       
thrpt   12  27416,212 ± 959,016  ops/s
iterAndMutResizableStruct                                       
thrpt   12  10204,870 ± 189,237  ops/s
iterAndMutSimpleSlidingWindowBuffer                             
thrpt   12  27627,217 ± 122,119  ops/s
iterAndMutStructArray                                           
thrpt   12  12714,642 ±  51,275  ops/s
iterAndMutStructArrayIndexed                                   
thrpt   12  11110,882 ±  26,910  ops/s
iterAndMutVanilla                                               
thrpt   12  27111,335 ± 661,822  ops/s
iterStruct                                                     
thrpt   12  13240,723 ±  40,612  ops/s
iterVanilla                                                     
thrpt   12  21452,188 ±  46,380  ops/s


All benchmarks iterate over a collection of 5000 Vector3f instances. iterAndMutVanilla is just a regular ArrayList iteration with forEach, setting the three components of each vector. iterAndMutStruct is my current implementation of a tight StructArray of Vector3fs with a sliding window iteration.

Vanilla Java iteration with mutation yields the baseline results with 27k operations. It's very intersting, that a non-abstracted simple version with a direct bytebuffer is three times as fast as the baseline, reaching 90k operations. Simple non-abstracted implementations with Kotlin's delegates brings us down to the baseline performance again. My struct abstraction in the current implementation with a struct array class implementation can only reach 50% of the baseline - quite a difference between the simple delegate approach and only a rough sixth of the simple direct bytebuffer approache's performance.

I have to figure out why my abstractions degrade performance by such amounts - the generated bytecode looks pretty similar for all the versions. At the time of writing, Kotlin's inline classes are not stable enough for delegate usage, so delegates cause some class overhead here.

But even though there are some performance differences in this very micro benchmark, it doesn't necessarily mean that other use cases show such dramatic differences as well. Additionally, the largest benefit my struct-alike implementation offers is, that now large and complex datastructures can be memcopied like this:


class MyStruct: Struct { // .. missing magic here val myFloat by FloatProperty() val mySecondFloat by FloatProperty() } val source = MyStruct().apply { myFloat = 5 } val target = MyStruct() source.copyTo(target) // Simple extension method that copies a bytebuffer println(target.myFloat) // prints 5

This means no iteration over nested arrays, complex copy constructors and even more complex nested invocation of them. Super handy for renderstate constructs in game engines - your whole renderstate instance can be mapped to a OpenGL struct and mapped as a shader storage buffer :)

Freitag, 15. Dezember 2017

Delegation and Ad-hoc polymorphism in Kotlin

CAUTION: This post got somewhat long :/ TLDR: You can effectively use Kotlin's delegation if you want to mimic ad-hoc polymorphism.

I was always fascinated by ad-hoc polymorphism. The clear separation between data and behavior that can for example be achieved with tools like Scala's type classes, is especially useful when combined with data oriented design, which is important for game development as well for example.

I realized, that the usefulness depends heavily on how this feature can be integrated and supported by the programming language you work with.

For example in Go, you don't have to implement an interface explicitly - if your 'class' (Go doesn't have classes) satisfies the contract of the interface, it is virtually implemented. That means you can safely pass your instance of your class into a method that expects an interface. I like that this is called static duck typing. This is a form of structural typing, because your instance is typed by parts of its definition. Structural typing has many advantages, but also the disadvantage that code can break or fail at runtime, when said interfaces change visibly or invisibly. Also, there's the potential of naming clashes, for example if two interfaces have methods with the same name/signature. Although I don't know anyhing about the internal implementation of Go's structural typing, I suggest that the whole system works without creating wrappers under the hood.

In languages like Java and Scala, we don't have this luxury, or rather we have to solve slightly different problems here. I find Scala's solution a very good one - but I don't know too many comparable others :) The type classes combined with implicits are a very good example of how to integrate this feature into a statically typed language (This is a very nice post that pretty much answers all questions about how those type classes and implicits in Scala work).

And here's the big but (and I can not lie..): The feature is very infamous by people that don't use Scala and even infamous by some people who do use it. My humble opinion is, that it boils down to the old problem: As a programmer, you read more code than you write. That should imply, that the ability to read code more easily is more important than the ability to write code easily. I would like to correct this statement slightly: It's more important to be able to easily read and understand what's happening. Very concise code is nice to read, because it hides a large amount of information - but there is a thin red line that should not be crossed when hiding information. Problem here is, that this is very subjective to a degree. One of the biggest critics to Scala is the complex implicit resolution, where people really struggle to understand it and even the compile times suffer drastically from it. This weakness is so clearly a problem not only for mediocre programmers, non-library developers but also for code quality of a project in general, that the Kotlin creators decided to leave ad-hoc polymorphism out of the language until now.

At the time of writing this, similar blog posts to my thoughts were created here and here. And in fact, even after reading the whole language improvement suggestion for ad-hoc polymorphism, I'm not quite sure if ad-hoc polymorphism without something comparable to Scala's implicits makes sense at all. Here's a small example of a situation where the usage of ad-hoc polymorphism would be appropriate: You have an interface and some implementations of this interface. The interface is consumed by some methods, one of them is someFrameWorkFunction:


interface MyInterface {  
    fun myInterfaceMethod()  
}  
class MiOwnImplementation : MyInterface {  
    override fun myInterfaceMethod() = println("This is my own implementation")  
}  
fun someFrameWorkFunction(parameter: MyInterface) = parameter.myInterfaceMethod()  
fun main(args: Array) {  
    someFrameWorkFunction(MiOwnImplementation())  
}  

Everything is fine until you have to integrate code that's outside of your control. As the following class, that may already have the functionality you need implemented, but with a wrong method signature and without your interface (because where should people get your interface from...):


class ForeignImplementation {  
    fun printSomething() = println("This is a foreign implementation")  
}  

Irrelevant which solution you take to integrate this foreign implementation, one thing is for sure: you need a description how the needed functions are implemented, hence a mapping from interface methods to implementation methods.

This could do the job in Kotlin, nothing fancy.


class MyFacade(private val impl: ForeignImplementation) : MyInterface {  
   override fun myInterfaceMethod() = impl.printSomething()  
}

Afterwards, we can use our implementation like this. Alternatively, we can use Kotlin's equivalent of an anonymous class, an object:


fun main(args: Array) {  
   someFrameWorkFunction(MiOwnImplementation())  
   someFrameWorkFunction(object : MyInterface {  
     private val impl = ForeignImplementation()  
     override fun myInterfaceMethod() = impl.printSomething()
  }
}

With an implicit resolution algorithm, one could write


someFrameWorkFunction(ForeignImplementation())  

instead of explicitly instantiating one's own facade.

The question here is: Is the implicit resolution worth the hassle that one now has to search for the conversion definition? Is the example with the explicit conversion that much worse readable? In my opinion, until now, there's no reason to not state the conversion explicitly. Only with taking the feature even further, it makes sense to have it - for example because you are used to using contextual objects as implicit parameters, as they are embedded in the language, like in Scala. For example it's possible to provide default implementations that can be imported by the user of your library. Without such a feature, one would have to provide overloads of a method that takes types as parameters. Kotlin won't benefit from it, instead it would be an artificial feature that is limited to one situation.
Swift on the other side allows for extensions, that can have an existing class implement an interface with some limitations I don't know in particular. Extensions are automatically available through module import. So they are somewhat comparable to what Scala can do, because your module can contain all the default extensions and make them available for the user.

In general, the separation of state and behavior could become the default option and ad-hoc polymorphism could be the default over parametric polymorphism. But on the JVM, you will have to pay for the wrapping class, so this is probably not the most wise idea. Extension functions on the other hand seem to extend a class's interface but does it via syntactic sugar and static dispatch. That means the extension can be imported on the callee's side, whereas class extensions or type classes are chosen by the caller.

So let's think about a more complex scenario. The interface our framework method accepts is a more complex one, that extends the List interface and provides a getLast() method that doesn't throw an exception if the list is empty, but returns a nullable reference. This can be a default implementation that uses an abstract property. Additionally, a real interface method is declared for prettyprinting, that classes have to implement.

interface MyList<T> : List<T> {  
   val list: List<T>  
   fun getLast() : T? = if (!list.isEmpty()) list.last() else null  
   fun prettyPrint()
}

Since one can only delegate to constructor parameters and new instances in Kotlin, there is no way to have MyList provide default implementations for all List methods just by delegating to the abstract property list. This limitation prohibits using a simple inline object declaration in a method that consumes a MyList interface because you would have to implement all methods of the List interface or create a local list outside of the statement.
A solution would be to create a simple Implementation (as one could and should typically with heavy use of delegation), that implements the List interface by itself with delegation, like:


class MyListImpl(override val list : List<T>) : MyList<T>, List<T> by list {  
   override fun prettyPrint() = println("Pretty: " + super.toString())  
}

So the framework method can be called like


someFrameWorkFunction(MyListImpl(emptyList()))

While this is not ad-hoc polymorphism by definition, under the hood it's equivalent to comparable solutions. Delegation can help to overcome many of the pain points when the need to satisfy interfaces with existing classes emerge. As for a general purpose usage of type classes, one has to keep in mind that delegation has a small runtime overhead, as it creates objects and delegates method calls.

Another very clean solution would be to use scopes extension methods to achieve the same thing. Usage of with can bring extensions defined on a interface into scope. Using a singleton would prevent us from creating objects, but obviously, one would have to pass the extension provider explicitly as a parameter.


interface ExtensionProvider<T> {
    fun T.customExtensionMethod()
}
object IntExtensionProvider : ExtensionProvider<Int> {
    override fun Int.customExtensionMethod() = println("Custom method prints: " + this)
}
fun <T> someFrameWorkFunction(list : List<T>, extensionProvider: ExtensionProvider<T>) {
    with(extensionProvider) {
        list.forEach { it.customExtensionMethod() }
    }
}

fun main(args: Array<String>) {
    someFrameWorkFunction(listOf(1,2,3), IntExtensionProvider)
}

And now I hope that I didn't confuse the wording of all these things :)

Freitag, 24. November 2017

Costs of Kotlin Delegation

If there is one thing I really really like about Kotlin, then it's the first class citizenship of delegation throughout the whole language. It provides a simple and concise way to have a replacement for inheritance. The nice thing here is, that abstract properties can be used in an interface, but the state remains in the implemting class, with very low amount of boilerplate, magic and chance of bullshit happening. Or an interface can be implemented by giving a delegating instance and that's it. Here's a short snippet to show what I am talking about (reduced example):

class Delegation(val myImpl: MyInterfaceImpl) : MyInterface by myImpl

Since this can be done in Java as well with default methods in interfaces and IDE code generation, it really is the reduced amount of boilerplate that has to be done to achieve it. It's only a worthy thing, if it can be expressed in readable, efficient code.

And the last one is a very important criteria: Efficient. I don't want to make my JVM code slower as it is already. We're talking about games - if another language is slower, than the code I can produce with Java is the better compromise for me. If I imagine using delegation over inheritance with Kotlin throughout my codebase, in every hierachy..will it slow down my engine remarkably? I ran benchmarks of implementations of a directly implemented field, an inherited field and a delegated field with Java and Kotlin. I expected a field to be faster than a property in general, and a delegated field to be slower alltogether. I used JMH and a blackhole, so there should be everything implemented just fine, but I get these unexpected results:

 Benchmark                     Mode Cnt     Score     Error Units  
 getStringJavaDelegation      thrpt 200 220181331,464 ± 2144028,358 ops/s  
 getStringJavaImplementation  thrpt 200 171078263,764 ± 889605,110 ops/s  
 getStringJavaInheritance      thrpt 200 170878616,220 ± 818848,070 ops/s  
 getStringKotlinDelegation     thrpt 200 225753956,507 ± 1740352,057 ops/s  
 getStringKotlinImplementation thrpt 200 168879795,813 ± 2728455,723 ops/s  
 getStringKotlinInheritance     thrpt 200 170414757,249 ± 1515476,325 ops/s  

Turns out the delegation to a delegate field is faster than the other two versions... Okay, I have the instantiation included in the benchmarked code, and even though I expected delegation to be slower right then, I removed it from the benchmark - so now a single getString() call is measured. Results:

 Benchmark                     Mode Cnt     Score     Error Units  
 getStringJavaDelegation      thrpt 200 301713586,642 ± 8160921,344 ops/s  
 getStringJavaImplementation  thrpt 200 225820433,449 ± 3676854,362 ops/s  
 getStringJavaInheritance      thrpt 200 234833613,665 ± 561919,892 ops/s  
 getStringKotlinDelegation     thrpt 200 320742908,021 ± 1406189,583 ops/s  
 getStringKotlinImplementation thrpt 200 230377534,877 ± 3347435,643 ops/s  
 getStringKotlinInheritance     thrpt 200 230821924,187 ± 1159446,814 ops/s  

No chance, same results. And additionally, Kotlin's delegation seem to be even faster then the bare hand implementation with Java. I decided to take a closer look at the bytecode.

 DelegationJava Bytecode  
  public getString()Ljava/lang/String;  
   L0  
   LINENUMBER 14 L0  
   ALOAD 0  
   GETFIELD de/hanno/playground/DelegationJava.impl : Lde/hanno/playground/MyInterfaceImplementation;  
   INVOKEVIRTUAL de/hanno/playground/MyInterfaceImplementation.getString ()Ljava/lang/String;  
   ARETURN  
   L1  
   LOCALVARIABLE this Lde/hanno/playground/DelegationJava; L0 L1 0  
   MAXSTACK = 1  
   MAXLOCALS = 1  


 DelegationKotlin Bytecode  
  public getString()Ljava/lang/String;  
   L0  
   ALOAD 0  
   GETFIELD de/hanno/playground/DelegationKotlin.myInterfaceImplementation : Lde/hanno/playground/MyInterfaceImplementation;  
   INVOKEVIRTUAL de/hanno/playground/MyInterfaceImplementation.getString ()Ljava/lang/String;  
   ARETURN  
   L1  
   LOCALVARIABLE this Lde/hanno/playground/DelegationKotlin; L0 L1 0  
   MAXSTACK = 1  
   MAXLOCALS = 1  
 

ImplementationJava ByteCode  
  public getString()Ljava/lang/String;  
   L0  
   LINENUMBER 14 L0  
   ALOAD 0  
   GETFIELD de/hanno/playground/ImplementationJava.myStringField : Ljava/lang/String;  
   ARETURN  
   L1  
   LOCALVARIABLE this Lde/hanno/playground/ImplementationJava; L0 L1 0  
   MAXSTACK = 1  
   MAXLOCALS = 1  

Not much difference here, just a LINENUMBER instruction. So I have to admit I'm very satisfied with this result, even though I can't explain it. Of course I know that one Bytecode is not everything ... but I don't feel like investing more time here because I have more interesting things to implement :) If anybody has further ideas here, I would like to hear.