Say Good Bye to JVM with Kotlin Native

Christopher Hay-Yin Ng
Black Lens
Published in
3 min readJul 26, 2017

ณ สถานแห่ง JVM

มาจะกล่าวถึง Kotlin แล้ว ทุกท่านคงทราบกันดีกว่า Kotlin สามารถใช้งานร่วมกับ Java เป็นอย่างดี เพราะตัวมันเองถูกสร้างเพื่อใช้รันอยู่บน JVM การใช้งาน Kotlin จึงมักจะเป็น Android หรือไม่ก็เป็นการสร้าง backend ซะมากกว่า (หรืออะไรก็ตามที่วิ่งเล่นบน JVM สุดที่รักของเราได้)

แล้วถ้าวันนึงเราไม่อยากใช้ JVM แล้วล่ะ???

ในบางงานเราอาจจะต้องบอกลากับ JVM ด้วยเหตุผลบางประการ เช่น

  • ต้องการใช้งาน library native ที่มีอยู่
  • รัน app แบบ native ตาม platform ต่างๆ
  • อยากใช้ทรัพยากรเครื่องให้มันสุดๆ
  • เบื่อ JVM

ด้วยเหตุผลเหล่านี้ทำให้เราต้องหาทางที่จะเอา Kotlin สุดรักของเรามาใช้งานแบบ native ให้ได้

Native คืออะไร

พูดถึง native มาตั้งหลายที แล้วสรุปมันคืออะไร มันต่างอะไรกับ JVM

native แปลตรงตัวคือพื้นเมือง ถ้าในระบบคอมพิวเตอร์ก็คือการรันชุดคำสั่งของเราโดยตรงบน platform นั้นๆ หรือที่เรียกว่า machine code ซึ่งแต่ละระบบก็จะมีรูปแบบต่างกันไป แม้จะเป็นการทำงานแบบเดียวกันก็ตาม

ส่วน JVM นั้นจะรับชุด byte code มา เพื่อทำการคิดต่อว่าจะรันคำสั่งนั้นๆอย่างไรต่อไป

ถ้าเทียบกันแล้ว การได้รันชุดคำสั่งโดยตรงย่อมดีกว่าผ่านระบบอื่นคั่นกลาง เพราะเท่ากับเราได้ควบคุมเครื่องอย่างแท้จริง

สวัสดี Kotlin Native

Kotlin Native คือตัวช่วยที่จะทำให้เราสามารถเขียน native app ด้วย Kotlin ได้ โดยตัวมันจะใช้ LLVM compiler เพื่อสร้าง machine code ตาม platform ที่เราต้องการ ซึ่งตอนนี้สามารถทำได้บน

  • Mac OS X
  • Linux
  • Apple iOS
  • Raspberry Pi
  • Windows
  • Web Assembly (ในอนาคต)

ว่าแล้วมาเริ่มสร้าง app แบบ Kotlin Native กันดีกว่า

Hello Native

เริ่มจาก app สามัญ ผมจะให้ app ปล่อยข้อความ Hello Native ออกมา ตัว project เราจะเลือกใช้แค่ Gradle โดยไม่มี plugin เสริมใดๆ เพราะตัว Kotlin Native ยังไม่มี plugin ตัวสมบูรณ์ให้เล่นกัน (ปัจจุบันยังอยู่ที่ 0.3)

New project with IntelliJ IDEA

สังเกตว่าเราไม่ได้เลือกแม้กระทั่งตัว Kotlin (Java) เพราะตัว stdlib ของ JVM นั้น ไม่สามารถใช้ร่วมกันได้กับตัว Native ทำให้เกิด error ในระหว่าง compile time ได้

เมื่อได้ project แล้ว เราก็มาตั้งค่าให้ Kotlin Native กันก่อน ใน build.gradle ตามนี้ครับ

จากการตั้งค่าข้างต้น จะมีสองส่วนหลักๆคือ ส่วนที่อยู่ใน buildscript เพื่อติดตั้ง kotlin-native-gradle-plugin และส่วน konanArtifacts ที่ใช้กำหนดค่าให้กับ Kotlin Native เช่น

  • target บอก platform ที่เราต้องการ
  • useInterop กำหนดนิยามของ library native ที่เราจะใช้ เช่น header
  • linkerOpts กำหนดตำแหน่งที่จะใช้งาน library native

กด sync gradle ซักรอบ พอเสร็จแล้วก็มาสร้างไฟล์ Kotlin กัน

code ถูกต้องตามมาตรฐาน จากนั้นก็ build ด้วยคำสั่ง

$ ./gradlew build

ถ้า build สำเร็จไปด้วยดี เราก็จะได้ executable app ใน folder build เช่นนี้

executable app path

จากนั้นก็ลองรันซะหน่อย

เพียงเท่านี้เราก็มี app แบบ native ที่เขียนด้วย Kotlin แล้ว สุดเก๋เลยทีเดียว

ประสานมือไปร่วมกันกับ C

จากตัวอย่างก่อนหน้า เราก็ได้ Kotlin app แบบ native แล้ว แต่มันก็คงดูธรรมดาไป ตัวอย่างต่อไปผมจะเสนอการ interop กับ library native ที่มีอยู่แล้ว เช่น ผมมี library algorithm ขั้นเทพที่สร้างด้วยภาษา C เพื่อใช้ในการแสดงคำว่า Hello World อยู่ มาดูกันว่าเราจะขอความร่วมมือต่างภาษาเช่นนี้อย่างไร

เริ่มจากการสร้าง lib hello ด้วย C กันก่อน โดยเริ่มที่ header เราจะมี function ชื่อ hello ที่ปล่อยข้อความออกมา

จากนั้นมาที่ตัว implement เราก็จะ return “Hello World\n” เพียงเท่านี้ก็เป็นอันเสร็จเรียบร้อย

เมื่อเตรียม code แล้ว ก็มา compile ให้เป็น shared lib ด้วยสองคำสั่งนี้ครับ

$ gcc -c hello.c
$ gcc -shared -o libhello.so hello.o

ถ้าทุกอย่างเรียบร้อย เราก็จะได้ libhello.so ไว้ใช้งาน ซึ่งไฟล์นี้จะถูกเรียกใช้งานจาก app native ของเราอีกที โดยให้เอาไฟล์นี้ไปวางไว้ที่ shared lib path เช่น

/usr/local/lib

ตอนนี้เราก็มี shared lib ไว้พร้อมเรียก Hello World แล้ว กลับมาที่ Kotlin อีกครั้ง เราจะมาแก้ไข gradle ให้สามารถเรียก lib จากข้างนอกได้ ด้วยการเรียกใช้ interop

สิ่งต้องเพิ่มอย่างแรกคือ konanInterop เพื่อใช้ชี้ def สำหรับกำหนดค่าการเรียกใช้ shared lib เช่น header, linker

ส่วนที่สองคือเพิ่ม useInterop เพื่อบอกว่าจะใช้ interop ตัวไหนที่เตรียมไว้ ตามด้วย linkerOpts เพื่อใช้ link หา shared lib ที่ build ไว้ก่อนหน้านี้

เตรียมพร้อม gradle เสร็จแล้ว มาสร้างไฟล์ def กันก่อน โดยการสร้าง package c_interop ไว้ในระดับเดียวกับ kotlin

c_interop path

ภายในไฟล์ hello.def กำหนดค่าตามนี้

จากตัวอย่างข้างต้นจะเป็นการระบุตำแหน่งของ header เพื่อให้ app ของเราเรียกใช้งาน lib ได้อย่างถูกต้อง

นอกจาก headers แล้ว เราสามารถระบุ linker ในนี้ได้เลย ในกรณีที่ต้องการ build หลายๆ target

เตรียม config ต่างๆเสร็จแล้ว ก็กลับมาแก้ไขไฟล์ Kotlin กันซักหน่อย

lib สำคัญที่ต้องเอาเข้ามาคือ kotlinx.cinterop เพื่อใช้ในการเรียก lib และ hello ซึ่งก็คือ interop ที่เราเตรียมไว้ ตรงจุดนี้ต้องระวังถ้ามีการใส่ kotlin/jvm ไว้ใน gradle จะทำให้เกิด syntax error ได้

การเรียกใช้งานสามารถเรียก function hello() ตรงๆได้เลย เสมือนกับว่าเราประกาศไว้ในไฟล์เดียวกัน จุดสังเกตคือ toKString() เนื่องจากตัว hello() นั้น return char * กลับมา ถ้า print ตรงๆเลยเราจะได้ CPointer ออกมา เพราะฉะนั้นต้องใช้ toKString() เรียกค่าออกมาซะก่อน

เสร็จแล้วก็ build, run กันอีกซักรอบ

ฮูเร เชื่อมต่อ shared lib กับ native app ได้อย่างสวยงาม

สรุป

แม้ว่าตอนนี้ Kotlin จะสามารถใช้งานข้างนอก JVM ได้แล้วก็ตาม แต่ก็ยังอยู่ในช่วงพัฒนา ยังอีกไกลสำหรับ production ที่สำคัญคือ Kotlin Native Stdlib นั้นยังไม่ออกมาแบบเต็มตัว

สำหรับ performance นั้น จากการลอง benchmark ดู เป็นเรื่องน่าตกใจว่า Kotlin Native ในตอนนี้ยังทำงานได้ช้ากว่า Kotlin JVM นัก ก็คงต้องรอดูกันต่อไปว่าอนาคตจะสามารถทำงานได้ดีขึ้นขนาดไหน

สำหรับบทความนี้ก็จบเพียงเท่านี้ ขอให้สนุกกับการ Kotlin ครับ

ตัวอย่างการใช้ Kotlin Native แบบอื่นๆ ติดตามได้ที่นี่ครับ

--

--