Slide 1

Slide 1 text

Kotlin/Native CLIs Ryan Harter @[email protected]

Slide 2

Slide 2 text

Differ

Slide 3

Slide 3 text

• Kotlin Multiplatform image dif f ing library • Like diff, but for images • Comparator backing Dropshots Differ

Slide 4

Slide 4 text

Differ interface ImageComparator { data class ComparisonResult( val pixelDifferences: Int, val pixelCount: Int, val width: Int, val height: Int, ) fun compare(left: Image, right: Image, diff: Mask? = null): ComparisonResult }

Slide 5

Slide 5 text

/ / Common interface Image { val width: Int val height: Int fun getPixel(x: Int, y: Int): Color } Differ

Slide 6

Slide 6 text

/ / JVM private class BufferedImageWrapper( private val delegate: BufferedImage ) : Image { override val width: Int = delegate.width override val height: Int = delegate.height override fun getPixel(x: Int, y: Int): Color = delegate.getRGB(x, y).let( :: Color) } Differ

Slide 7

Slide 7 text

private class BufferedImageWrapper( / / Android internal class BitmapImage( private val src: Bitmap ) : Image { override val width: Int get() = src.width override val height: Int get() = src.height override fun getPixel(x: Int, y: Int): Color = Color(src.getPixel(x, y)) }

Slide 8

Slide 8 text

Differ

Slide 9

Slide 9 text

Differ

Slide 10

Slide 10 text

Differ Di ff er

Slide 11

Slide 11 text

Differ Di ff er Dropshots

Slide 12

Slide 12 text

Differ Di ff er Dropshots differ CLI

Slide 13

Slide 13 text

Differ Di ff er Dropshots differ rharter $ ./differ

Slide 14

Slide 14 text

Differ ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png

Slide 15

Slide 15 text

Differ ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Images are different 222407 pixels are different Time: 338ms Differences: 222407 (8.044234%) FAIL ⬢[rharter@toolbox differ]$

Slide 16

Slide 16 text

Differ ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png

Slide 17

Slide 17 text

Differ ⬢[rharter@toolbox differ]$ $JAVA_HOME/jre/sh/java -classpath ?? ? \ 
 com.dropbox.differ.cli.MainKt -- verbose cat.png cat-bad.png

Slide 18

Slide 18 text

Differ ⬢[rharter@toolbox differ]$ brew install java ⬢[rharter@toolbox differ]$ $JAVA_HOME/jre/sh/java -classpath

Slide 19

Slide 19 text

Differ ⬢[rharter@toolbox differ]$ brew install java ⬢[rharter@toolbox differ]$ unzip all_the_jars.zip ⬢[rharter@toolbox differ]$ $JAVA_HOME/jre/sh/java -classpath

Slide 20

Slide 20 text

Differ ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Images are different 222407 pixels are different Time: 338ms Differences: 222407 (8.044234%) FAIL ⬢[rharter@toolbox differ]$

Slide 21

Slide 21 text

Differ ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Images are different 222407 pixels are different Time: 338ms Differences: 222407 (8.044234%) FAIL ⬢[rharter@toolbox differ]$ Time: 338ms

Slide 22

Slide 22 text

Kotlin/Native for CLIs?

Slide 23

Slide 23 text

• Can we make it feel native? • How do we handle dependencies? Single binary? • Can we make distribution easier? • Can we get better performance? Kotlin/Native for CLIs?

Slide 24

Slide 24 text

Migrating Differ CLI to Kotlin/Native

Slide 25

Slide 25 text

dependencies { implementation(libs.kotlinx.cli) implementation(libs.kotlinx.io) implementation(project(":differ")) } application { mainClass.set("com.dropbox.differ.cli.MainKt") } plugins { alias(libs.plugins.kotlin.jvm) id("application") }

Slide 26

Slide 26 text

dependencies { implementation(libs.kotlinx.cli) implementation(libs.kotlinx.io) implementation(project(":differ")) } application { mainClass.set("com.dropbox.differ.cli.MainKt") } plugins { alias(libs.plugins.kotlin.multiplatform) id("application") }

Slide 27

Slide 27 text

kotlin { jvm { withJava() } macosX64() macosArm64() linuxX64() sourceSets { val commonMain by getting { dependencies { implementation(libs.kotlinx.cli) implementation(libs.kotlinx.io) implementation(project(":differ")) } application { } } } }

Slide 28

Slide 28 text

dependencies { implementation(libs.kotlinx.cli) implementation(libs.kotlinx.io) implementation(project(":differ")) } } application { mainClass.set("com.dropbox.differ.cli.MainKt") } } } targets.withType { binaries { executable { baseName = "differ" entryPoint = "com.dropbox.differ.cli.main" } } }

Slide 29

Slide 29 text

Dependencies

Slide 30

Slide 30 text

Dependencies differ CLI

Slide 31

Slide 31 text

Dependencies Di ff er differ CLI

Slide 32

Slide 32 text

Dependencies Di ff er differ CLI kotlinx-cli

Slide 33

Slide 33 text

Dependencies Di ff er differ CLI kotlinx-cli image loading?

Slide 34

Slide 34 text

Dependencies

Slide 35

Slide 35 text

Dependencies

Slide 36

Slide 36 text

Dependencies Static

Slide 37

Slide 37 text

Dependencies Static Dynamic

Slide 38

Slide 38 text

Dependencies Static Dynamic

Slide 39

Slide 39 text

Executable Executable Dependencies Static Dynamic

Slide 40

Slide 40 text

Executable Executable Dependencies Static Dynamic

Slide 41

Slide 41 text

Executable Executable Dependencies Static Dynamic Operating System

Slide 42

Slide 42 text

Executable Executable Dependencies Static Dynamic

Slide 43

Slide 43 text

Dependencies Static Dynamic Executable Executable Executable Executable Executable Executable Executable Executable

Slide 44

Slide 44 text

Dependencies Static Dynamic stb_image libpng

Slide 45

Slide 45 text

stb_image https://github.com/nothings/stb

Slide 46

Slide 46 text

single- f ile public domain (or MIT licensed) libraries for C/C++ stb_image https://github.com/nothings/stb

Slide 47

Slide 47 text

stb_image https://github.com/nothings/stb

Slide 48

Slide 48 text

# stbimage.def headers = stb_image.h headerFilter = stb_image.h package = stb.image - -- #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" stb_image https://github.com/nothings/stb

Slide 49

Slide 49 text

# stbimage.def headers = stb_image.h headerFilter = stb_image.h package = stb.image - -- #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" headers = stb_image.h stb_image https://github.com/nothings/stb

Slide 50

Slide 50 text

# stbimage.def headers = stb_image.h headerFilter = stb_image.h package = stb.image - -- #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" headerFilter = stb_image.h stb_image https://github.com/nothings/stb

Slide 51

Slide 51 text

# stbimage.def headers = stb_image.h headerFilter = stb_image.h package = stb.image - -- #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" package = stb.image stb_image https://github.com/nothings/stb

Slide 52

Slide 52 text

# stbimage.def headers = stb_image.h headerFilter = stb_image.h package = stb.image - -- #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" - -- stb_image https://github.com/nothings/stb

Slide 53

Slide 53 text

# stbimage.def headers = stb_image.h headerFilter = stb_image.h package = stb.image - -- #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" stb_image https://github.com/nothings/stb

Slide 54

Slide 54 text

/ * stb_image - v2.27 - public domain image loader - http: // nothings.org/stb no warranty implied; use at your own risk Do this: #define STB_IMAGE_IMPLEMENTATION before you include this file in *one* C or C ++ file to create the implementation. / / i.e. it should look like this: #include .. . #include .. . #include .. . #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free stb_image https://github.com/nothings/stb

Slide 55

Slide 55 text

/ / Basic usage (see HDR discussion below for HDR usage): / / int x,y,n; / / unsigned char *data = stbi_load(filename, &x, &y, &n, 0); / / // ... process data if not NULL ... / / // ... x = width, y = height, n = # 8-bit components per pixel ... / / // ... replace '0' with '1' .. '4' to force that many components per pixel / / // ... but 'n' will always be the number that it would have been if you said 0 / / stbi_image_free(data) stb_image https://github.com/nothings/stb

Slide 56

Slide 56 text

class StbImage internal constructor(filePath: String) : Image { } stb_image https://github.com/nothings/stb

Slide 57

Slide 57 text

class StbImage internal constructor(filePath: String) : Image { } override val width: Int override val height: Int stb_image https://github.com/nothings/stb

Slide 58

Slide 58 text

class StbImage internal constructor(filePath: String) : Image { } override val width: Int override val height: Int private val pointer: CPointer / / unsigned char * stb_image https://github.com/nothings/stb

Slide 59

Slide 59 text

class StbImage internal constructor(filePath: String) : Image { } override val width: Int override val height: Int private val pointer: CPointer / / unsigned char * init { } stb_image https://github.com/nothings/stb

Slide 60

Slide 60 text

class StbImage internal constructor(filePath: String) : Image { } override val width: Int override val height: Int private val pointer: CPointer / / unsigned char * init { } val data = stbi_load(filePath, ? ? ? , , , 0) stb_image https://github.com/nothings/stb

Slide 61

Slide 61 text

class StbImage internal constructor(filePath: String) : Image { } override val width: Int override val height: Int private val pointer: CPointer / / unsigned char * init { } pointer = data ?. reinterpret() ?: throw Exception("Failed to read image from file: $filePath") val data = stbi_load(filePath, ? ? ? , , , 0) stb_image https://github.com/nothings/stb

Slide 62

Slide 62 text

class StbImage internal constructor(filePath: String) : Image { } override val width: Int :

Slide 63

Slide 63 text

class StbImage internal constructor(filePath: String) : Image { } override val width: Int override val height: Int private val pointer: CPointer / / unsigned char * init { } pointer = data ?. reinterpret() ?: throw Exception("Failed to read image from file: $filePath") val data = stbi_load(filePath, ? ? ? , , , 0) stb_image https://github.com/nothings/stb

Slide 64

Slide 64 text

class StbImage internal constructor(filePath: String) : Image { } override val width: Int override val height: Int private val pointer: CPointer / / unsigned char * init { } pointer = data ?. reinterpret() ?: throw Exception("Failed to read image from file: $filePath") val arena = Arena() try { } finally { arena.clear() } val data = stbi_load(filePath, ? ? ? , , , 0) stb_image https://github.com/nothings/stb

Slide 65

Slide 65 text

} private val pointer: CPointer / / unsigned char * init { } pointer = data ?. reinterpret() ?: throw Exception("Failed to read image from file: $filePath") val arena = Arena() try { } finally { arena.clear() } val x = arena.alloc() val y = arena.alloc() val n = arena.alloc() val data = stbi_load(filePath, ? ? ? , , , 0) stb_image https://github.com/nothings/stb

Slide 66

Slide 66 text

} private val pointer: CPointer / / unsigned char * init { } val data = stbi_load(filePath, pointer = data ?. reinterpret() ?: throw Exception("Failed to read image from file: $filePath") val arena = Arena() try { } finally { arena.clear() } val x = arena.alloc() val y = arena.alloc() val n = arena.alloc() , , , 0) x.ptr y.ptr n.ptr stb_image https://github.com/nothings/stb

Slide 67

Slide 67 text

} } val data = stbi_load(filePath, pointer = data ?. reinterpret() ?: throw Exception("Failed to read image from file: $filePath") } finally { arena.clear() } val x = arena.alloc() val y = arena.alloc() val n = arena.alloc() , , , 0) x.ptr y.ptr n.ptr width = x.value height = y.value channels = n.value stb_image https://github.com/nothings/stb

Slide 68

Slide 68 text

} } ?: throw Exception("Failed to read image from file: $filePath") } finally { arena.clear() } width = x.value height = y.value channels = n.value override fun getPixel(x: Int, y: Int): Color { } stb_image https://github.com/nothings/stb

Slide 69

Slide 69 text

} } ?: throw Exception("Failed to read image from file: $filePath") } finally { arena.clear() } width = x.value height = y.value channels = n.value override fun getPixel(x: Int, y: Int): Color { } val pixelOffset = (x + y * width) * channels stb_image https://github.com/nothings/stb

Slide 70

Slide 70 text

} } } finally { arena.clear() } height = y.value channels = n.value override fun getPixel(x: Int, y: Int): Color { } val pixelOffset = (x + y * width) * channels return Color( r = pointer[pixelOffset + 0], g = pointer[pixelOffset + 1], b = pointer[pixelOffset + 2], a = pointer[pixelOffset + 3], ) stb_image https://github.com/nothings/stb

Slide 71

Slide 71 text

} } val pixelOffset = (x + y * width) * channels return Color( r = pointer[pixelOffset + 0], g = pointer[pixelOffset + 1], b = pointer[pixelOffset + 2], a = pointer[pixelOffset + 3], ) stb_image https://github.com/nothings/stb fun close() { stbi_image_free(pointer) }

Slide 72

Slide 72 text

class StbImage internal constructor(filePath: String) : Image { override val width: Int override val height: Int private val pointer: CPointer / / unsigned char * init { val data = stbi_load(filePath, pointer = data ?. reinterpret() ?: throw Exception("Failed to read image from file: $filePath") val arena = Arena() try { val x = arena.alloc() val y = arena.alloc() val n = arena.alloc() , , , 0) x.ptr y.ptr n.ptr width = x.value height = y.value stb_image https://github.com/nothings/stb

Slide 73

Slide 73 text

Dependencies Static Dynamic stb_image libpng

Slide 74

Slide 74 text

libpng http://www.libpng.org/

Slide 75

Slide 75 text

libpng http://www.libpng.org/ libpng is the of f icial PNG reference library.

Slide 76

Slide 76 text

libpng http://www.libpng.org/

Slide 77

Slide 77 text

libpng http://www.libpng.org/ # libpng.def headers = png.h headerFilter = png.h package = libpng compilerOpts.osx = -I/usr/local/include -I/opt/homebrew/include/libpng16 compilerOpts.linux = -I/usr/include/libpng -I/usr/include/libpng16 linkerOpts.osx = -L/usr/local/lib -L/opt/homebrew/lib -lpng linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu -lpng

Slide 78

Slide 78 text

libpng http://www.libpng.org/ # libpng.def headers = png.h headerFilter = png.h package = libpng compilerOpts.osx = -I/usr/local/include -I/opt/homebrew/include/libpng16 compilerOpts.linux = -I/usr/include/libpng -I/usr/include/libpng16 linkerOpts.osx = -L/usr/local/lib -L/opt/homebrew/lib -lpng linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu -lpng headers = png.h headerFilter = png.h package = libpng

Slide 79

Slide 79 text

libpng http://www.libpng.org/ # libpng.def headers = png.h headerFilter = png.h package = libpng compilerOpts.osx = -I/usr/local/include -I/opt/homebrew/include/libpng16 compilerOpts.linux = -I/usr/include/libpng -I/usr/include/libpng16 linkerOpts.osx = -L/usr/local/lib -L/opt/homebrew/lib -lpng linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu -lpng compilerOpts.osx = -I/usr/local/include -I/opt/homebrew/include/libpng16 compilerOpts.linux = -I/usr/include/libpng -I/usr/include/libpng16

Slide 80

Slide 80 text

libpng http://www.libpng.org/ # libpng.def headers = png.h headerFilter = png.h package = libpng compilerOpts.osx = -I/usr/local/include -I/opt/homebrew/include/libpng16 compilerOpts.linux = -I/usr/include/libpng -I/usr/include/libpng16 linkerOpts.osx = -L/usr/local/lib -L/opt/homebrew/lib -lpng linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu -lpng linkerOpts.osx = -L/usr/local/lib -L/opt/homebrew/lib -lpng linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu -lpng

Slide 81

Slide 81 text

class PNGImage(val filePath: String) : Image { private val arena = Arena() private val data: CPointer override val width: Int override val height: Int init { val loadArena = Arena() try { val realPath = memScoped { val tmp = allocArray(PATH_MAX) realpath(filePath, tmp) tmp.toKString() } val fp = fopen(realPath, "rb") ?: error("Failed to open file pointer at $realPath") libpng http://www.libpng.org/

Slide 82

Slide 82 text

private fun getChannel(x: Int, y: Int, channel: Int): UByte { val pixelOffset = (x + y * width) * 4 return data[pixelOffset + channel] } override fun getPixel(x: Int, y: Int): Color = Color( r = getChannel(x, y, 0), g = getChannel(x, y, 0), b = getChannel(x, y, 0), a = getChannel(x, y, 0), ) fun close() { arena.clear() } } libpng http://www.libpng.org/

Slide 83

Slide 83 text

class PNGImage(val filePath: String) : Image { private val arena = Arena() private val data: CPointer override val width: Int override val height: Int init { val loadArena = Arena() try { val realPath = memScoped { val tmp = allocArray(PATH_MAX) realpath(filePath, tmp) tmp.toKString() } val fp = fopen(realPath, "rb") ?: error("Failed to open file pointer at $realPath") libpng http://www.libpng.org/

Slide 84

Slide 84 text

Dependencies Static Dynamic stb_image libpng

Slide 85

Slide 85 text

Dependencies Static Dynamic stb_image libpng JVM ImageIO

Slide 86

Slide 86 text

ImageIO javax.image

Slide 87

Slide 87 text

ImageIO javax.image private class BufferedImageWrapper( override fun getPixel(x: Int, y: Int): Color = delegate.getRGB(x, y).let( :: Color) ) : Image { private val delegate: BufferedImage / / JVM override val width: Int = delegate.width override val height: Int = delegate.height }

Slide 88

Slide 88 text

ImageIO javax.image private class BufferedImageWrapper( override fun getPixel(x: Int, y: Int): Color = delegate.getRGB(x, y).let( :: Color) ) : Image { private val delegate: BufferedImage / / JVM override val width: Int = delegate.width override val height: Int = delegate.height } BufferedImage

Slide 89

Slide 89 text

ImageIO javax.image private class BufferedImageWrapper( override fun getPixel(x: Int, y: Int): Color = delegate.getRGB(x, y).let( :: Color) ) : Image { private val delegate: BufferedImage / / JVM override val width: Int = delegate.width override val height: Int = delegate.height }

Slide 90

Slide 90 text

ImageIO javax.image private class BufferedImageWrapper( override fun getPixel(x: Int, y: Int): Color = delegate.getRGB(x, y).let( :: Color) ) : Image { private val delegate: BufferedImage / / JVM override val width: Int = delegate.width override val height: Int = delegate.height } fun Image(filePath: String): Image = BufferedImageWrapper(ImageIO.read(File(filePath)))

Slide 91

Slide 91 text

ImageIO javax.image private class BufferedImageWrapper( override fun getPixel(x: Int, y: Int): Color = delegate.getRGB(x, y).let( :: Color) ) : Image { private val delegate: BufferedImage / / JVM override val width: Int = delegate.width override val height: Int = delegate.height } fun Image(filePath: String): Image = BufferedImageWrapper(ImageIO.read(File(filePath))) ImageIO.read(File(filePath))

Slide 92

Slide 92 text

Dependencies Static Dynamic stb_image libpng JVM ImageIO

Slide 93

Slide 93 text

Performance

Slide 94

Slide 94 text

Performance Static Dynamic stb_image libpng

Slide 95

Slide 95 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 96ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 95ms Starting comparison Finished comparison in 1609ms Images are different 222407 pixels are different Time: 1811ms Differences: 222407 (8.044234%) FAIL

Slide 96

Slide 96 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 96ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 95ms Starting comparison Finished comparison in 1609ms Images are different 222407 pixels are different Time: 1811ms Differences: 222407 (8.044234%) FAIL Loaded ./differ/src/commonTest/resources/cat.png in 96ms Loaded ./differ/src/commonTest/resources/cat-bad.png in 95ms

Slide 97

Slide 97 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 96ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 95ms Starting comparison Finished comparison in 1609ms Images are different 222407 pixels are different Time: 1811ms Differences: 222407 (8.044234%) FAIL Loaded ./differ/src/commonTest/resources/cat.png in 96ms Loaded ./differ/src/commonTest/resources/cat-bad.png in 95ms Finished comparison in 1609ms

Slide 98

Slide 98 text

Performance Static Dynamic stb_image libpng

Slide 99

Slide 99 text

Performance Static Dynamic stb_image libpng Load: 95ms Comparison: 1609ms

Slide 100

Slide 100 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 43ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 43ms Starting comparison Finished comparison in 1602ms Images are different 149973 pixels are different Time: 1712ms Differences: 149973 (5.424371%) FAIL libpng http://www.libpng.org/

Slide 101

Slide 101 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 43ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 43ms Starting comparison Finished comparison in 1602ms Images are different 149973 pixels are different Time: 1712ms Differences: 149973 (5.424371%) FAIL libpng http://www.libpng.org/ Loaded ./differ/src/commonTest/resources/cat.png in 43ms Loaded ./differ/src/commonTest/resources/cat-bad.png in 43ms

Slide 102

Slide 102 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 43ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 43ms Starting comparison Finished comparison in 1602ms Images are different 149973 pixels are different Time: 1712ms Differences: 149973 (5.424371%) FAIL libpng http://www.libpng.org/ Loaded ./differ/src/commonTest/resources/cat.png in 43ms Loaded ./differ/src/commonTest/resources/cat-bad.png in 43ms Finished comparison in 1602ms

Slide 103

Slide 103 text

Performance Static Dynamic stb_image libpng Load: 95ms Comparison: 1609ms

Slide 104

Slide 104 text

Performance Static Dynamic stb_image libpng Load: 95ms Comparison: 1609ms Load: 43ms Comparison: 1602ms

Slide 105

Slide 105 text

Performance Static Dynamic JVM ImageIO stb_image libpng Load: 95ms Comparison: 1609ms Load: 43ms Comparison: 1602ms

Slide 106

Slide 106 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 136ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 77ms Starting comparison Finished comparison in 188ms Images are different 222407 pixels are different Time: 407ms Differences: 222407 (8.044234%) FAIL ImageIO javax.image

Slide 107

Slide 107 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 136ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 77ms Starting comparison Finished comparison in 188ms Images are different 222407 pixels are different Time: 407ms Differences: 222407 (8.044234%) FAIL ImageIO javax.image Loaded ./differ/src/commonTest/resources/cat.png in 136ms Loaded ./differ/src/commonTest/resources/cat-bad.png in 77ms

Slide 108

Slide 108 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 136ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 77ms Starting comparison Finished comparison in 188ms Images are different 222407 pixels are different Time: 407ms Differences: 222407 (8.044234%) FAIL ImageIO javax.image Loaded ./differ/src/commonTest/resources/cat.png in 136ms Loaded ./differ/src/commonTest/resources/cat-bad.png in 77ms Finished comparison in 188ms

Slide 109

Slide 109 text

Performance Static Dynamic JVM ImageIO stb_image libpng Load: 95ms Comparison: 1609ms Load: 43ms Comparison: 1602ms

Slide 110

Slide 110 text

Performance Static Dynamic JVM ImageIO stb_image libpng Load: 95ms Comparison: 1609ms Load: 43ms Comparison: 1602ms Load: ~110ms Comparison: 188ms

Slide 111

Slide 111 text

Distribution

Slide 112

Slide 112 text

Executable Executable Distribution Static Dynamic Operating System

Slide 113

Slide 113 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ

Slide 114

Slide 114 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ |====================================================>| 100%

Slide 115

Slide 115 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ |====================================================>| 100% ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png

Slide 116

Slide 116 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ |====================================================>| 100% ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 96ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 95ms Starting comparison Finished comparison in 1609ms Images are different 222407 pixels are different Time: 1811ms Differences: 222407 (8.044234%)

Slide 117

Slide 117 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ

Slide 118

Slide 118 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ-macosArm64

Slide 119

Slide 119 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ-macosArm64 ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ-macosX86 or

Slide 120

Slide 120 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ-macosArm64 ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ-macosX86 or ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ-linuxX64 or

Slide 121

Slide 121 text

stb_image https://github.com/nothings/stb ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ-macosArm64 ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ-macosX86 or ⬢[rharter@toolbox differ]$ curl -o ./differ https: // example.com/differ-linuxX64 or or

Slide 122

Slide 122 text

Executable Executable Distribution Static Dynamic Operating System

Slide 123

Slide 123 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png libpng http://www.libpng.org/

Slide 124

Slide 124 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png ./differ: error while loading shared libraries: libpng16.so.16: cannot open shared object file: No such file or directory ⬢[rharter@toolbox differ]$ libpng http://www.libpng.org/

Slide 125

Slide 125 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png ./differ: error while loading shared libraries: libpng16.so.16: cannot open shared object file: No such file or directory ⬢[rharter@toolbox differ]$ libpng http://www.libpng.org/ sudo dnf install -y libpng

Slide 126

Slide 126 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png ./differ: error while loading shared libraries: libpng16.so.16: cannot open shared object file: No such file or directory ⬢[rharter@toolbox differ]$ libpng http://www.libpng.org/ Doing things and working hard ... sudo dnf install -y libpng

Slide 127

Slide 127 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png ./differ: error while loading shared libraries: libpng16.so.16: cannot open shared object file: No such file or directory ⬢[rharter@toolbox differ]$ libpng http://www.libpng.org/ sudo dnf install -y libpng Doing things and working hard ... ⬢[rharter@toolbox differ]$

Slide 128

Slide 128 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png ./differ: error while loading shared libraries: libpng16.so.16: cannot open shared object file: No such file or directory ⬢[rharter@toolbox differ]$ libpng http://www.libpng.org/ sudo dnf install -y libpng Doing things and working hard ... ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png

Slide 129

Slide 129 text

⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png ./differ: error while loading shared libraries: libpng16.so.16: cannot open shared object file: No such file or directory ⬢[rharter@toolbox differ]$ libpng http://www.libpng.org/ sudo dnf install -y libpng Doing things and working hard ... ⬢[rharter@toolbox differ]$ ./differ -- verbose cat.png cat-bad.png Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 43ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 43ms Starting comparison Finished comparison in 1602ms Images are different

Slide 130

Slide 130 text

Executable Executable Distribution Static Dynamic Operating System

Slide 131

Slide 131 text

Distribution Static JVM Executable Operating System Executable Dynamic

Slide 132

Slide 132 text

Distribution Static Dynamic JVM Executable Operating System Executable Executable

Slide 133

Slide 133 text

ImageIO javax.image ⬢[rharter@toolbox differ]$ java \

Slide 134

Slide 134 text

ImageIO javax.image ⬢[rharter@toolbox differ]$ java \ -classpath $APP_HOME/lib/cli-jvm-0.0.2-SNAPSHOT.jar:$APP_HOME/lib/ kotlinx-io-0.1.16.jar:$APP_HOME/lib/differ-jvm-0.0.2-SNAPSHOT.jar: $APP_HOME/lib/kotlinx-cli-jvm-0.3.4.jar:$APP_HOME/lib/kotlin-stdlib- jdk8-1.8.0.jar:$APP_HOME/lib/kotlin-stdlib-jdk7-1.8.0.jar:$APP_HOME/lib/ kotlin-stdlib-1.8.0.jar:$APP_HOME/lib/kotlin-stdlib-common-1.8.0.jar: $APP_HOME/lib/atomicfu-common-0.14.1.jar:$APP_HOME/lib/ annotations-13.0.jar \

Slide 135

Slide 135 text

ImageIO javax.image ⬢[rharter@toolbox differ]$ java \ -classpath $APP_HOME/lib/cli-jvm-0.0.2-SNAPSHOT.jar:$APP_HOME/lib/ kotlinx-io-0.1.16.jar:$APP_HOME/lib/differ-jvm-0.0.2-SNAPSHOT.jar: $APP_HOME/lib/kotlinx-cli-jvm-0.3.4.jar:$APP_HOME/lib/kotlin-stdlib- jdk8-1.8.0.jar:$APP_HOME/lib/kotlin-stdlib-jdk7-1.8.0.jar:$APP_HOME/lib/ kotlin-stdlib-1.8.0.jar:$APP_HOME/lib/kotlin-stdlib-common-1.8.0.jar: $APP_HOME/lib/atomicfu-common-0.14.1.jar:$APP_HOME/lib/ annotations-13.0.jar \ com.dropbox.differ.cli.jvm.JvmMainKt \

Slide 136

Slide 136 text

ImageIO javax.image ⬢[rharter@toolbox differ]$ java \ -classpath $APP_HOME/lib/cli-jvm-0.0.2-SNAPSHOT.jar:$APP_HOME/lib/ kotlinx-io-0.1.16.jar:$APP_HOME/lib/differ-jvm-0.0.2-SNAPSHOT.jar: $APP_HOME/lib/kotlinx-cli-jvm-0.3.4.jar:$APP_HOME/lib/kotlin-stdlib- jdk8-1.8.0.jar:$APP_HOME/lib/kotlin-stdlib-jdk7-1.8.0.jar:$APP_HOME/lib/ kotlin-stdlib-1.8.0.jar:$APP_HOME/lib/kotlin-stdlib-common-1.8.0.jar: $APP_HOME/lib/atomicfu-common-0.14.1.jar:$APP_HOME/lib/ annotations-13.0.jar \ com.dropbox.differ.cli.jvm.JvmMainKt \ - - verbose cat.png cat-bad.png

Slide 137

Slide 137 text

ImageIO javax.image ⬢[rharter@toolbox differ]$ java -jar cli-jvm-0.0.2.jar \ - - verbose cat.png cat-bad.png

Slide 138

Slide 138 text

ImageIO javax.image ⬢[rharter@toolbox differ]$ ./gradlew tasks

Slide 139

Slide 139 text

ImageIO javax.image ⬢[rharter@toolbox differ]$ ./gradlew tasks . . . Distribution tasks ------------------ assembleDist - Assembles the main distributions distTar - Bundles the project as a distribution. distZip - Bundles the project as a distribution. installDist - Installs the project as a distribution as-is. . . .

Slide 140

Slide 140 text

ImageIO javax.image ⬢[rharter@toolbox differ]$ ./gradlew tasks . . . Distribution tasks ------------------ assembleDist - Assembles the main distributions distTar - Bundles the project as a distribution. distZip - Bundles the project as a distribution. installDist - Installs the project as a distribution as-is. . . . installDist

Slide 141

Slide 141 text

ImageIO javax.image

Slide 142

Slide 142 text

ImageIO javax.image

Slide 143

Slide 143 text

- - verbose cat.png cat-bad.png ⬢[rharter@toolbox differ]$ ./differ ImageIO javax.image

Slide 144

Slide 144 text

- - verbose cat.png cat-bad.png ⬢[rharter@toolbox differ]$ ./bin/cli ImageIO javax.image

Slide 145

Slide 145 text

- - verbose cat.png cat-bad.png ⬢[rharter@toolbox differ]$ ./bin/cli Starting to load first image: ./differ/src/commonTest/resources/cat.png Loaded ./differ/src/commonTest/resources/cat.png in 136ms Starting to load second image: ./differ/src/commonTest/resources/cat-bad.png Loaded ./differ/src/commonTest/resources/cat-bad.png in 77ms Starting comparison Finished comparison in 188ms Images are different 222407 pixels are different Time: 407ms Differences: 222407 (8.044234%) FAIL ImageIO javax.image

Slide 146

Slide 146 text

Conclusion

Slide 147

Slide 147 text

Conclusion

Slide 148

Slide 148 text

Conclusion • Native dependencies are easier

Slide 149

Slide 149 text

Conclusion • Native dependencies are easier

Slide 150

Slide 150 text

Conclusion • Native dependencies are easier • Native performance is always better

Slide 151

Slide 151 text

Conclusion • Native dependencies are easier • Native performance is always better

Slide 152

Slide 152 text

Conclusion • Native dependencies are easier • Native performance is always better • Native distribution is easier

Slide 153

Slide 153 text

Conclusion • Native dependencies are easier • Native performance is always better • Native distribution is easier

Slide 154

Slide 154 text

Conclusion • Native dependencies are easier • Native performance is always better • Native distribution is easier • You should test your assumptions to see what works for you

Slide 155

Slide 155 text

Kotlin/Native CLIs Ryan Harter @[email protected]