Screen Shot 2559-01-09 at 5.37.01 PM
ปัญหาหนึ่งของการพัฒนา Android application ก็คือ
เวลาในการ build project ด้วย gradle มันนานมาก ๆ
ยิ่ง project มีขนาดใหญ่ขึ้น
ยิ่ง project มี module จำนวนมาก
ยิ่ง project ใช้ library จำนวนมาก

คำถามคือ จะแก้ไข หรือ ปรับปรุงอย่างไรดี ?
เพื่อทำให้เวลาการ build มันต่ำลงให้ได้มากที่สุด
เรามาค้นหาวิธีการไปพร้อม ๆ กันเลย

นักพัฒนาอย่างเรา ต้องเริ่มต้นด้วยการค้นหาที่ Google สิ !!

ผลที่ได้มันเยอะมาก ๆ
ถ้าไม่เชื่อลองดูที่ส่วนของ Reference Website ด้านล่างสุดนะ
บางคนนำไปใช้แล้วอาจจะลดเวลาได้บ้าง หรือ ไม่ได้ แล้วแต่กรณีกันไป

แต่ก่อนอื่นแนะนำให้ดูสถานะปัจจุบันของการ build ของ project ก่อนนะ

โดย Environment ที่ผมใช้ในการทดสอบคือ

  • Macbook Pro, RAM 8GB แบบเหลือนิดเดียว, CPU 2.4 GHz
  • Android Studio 2.0 Preview 4
  • Grade 2.8

ขั้นแรกใช้ profile มาช่วยดูเวลาการ build ในปัจจุบัน
$./gradlew clean assembleDevDebug --profile

และ

$./gradlew assembleDevDebug --profile

ข้อสังเกต

  • การใช้ clean task นั้นเป็นการ build from scratch เป็นการ build ใหม่ทั้งหมด
  • แต่ถ้าไม่ใช้ clean จะเรียกว่า incremental build สำหรับการ run ซ้ำ จะทำให้ build เร็วขึ้น ซึ่งใช้งานบ่อยกว่าสำหรับการพัฒนา

ได้ผลการทำงานดังนี้
เป็นข้อมูลที่ทุกคนควรจดบันทึก และ จดจำไว้เลย
มันเป็นข้อมูลตั้งต้น ก่อนทำการปรับปรุงส่วนอื่น ๆ
ปล. ให้ทำการ run อย่างน้อย 3 ครั้งนะ
เพื่อทำให้มั่นใจว่าผลที่ได้มันถูกต้อง และ ใกล้เคียงกันมากที่สุด

ได้เวลาการทำงานที่แย่ที่สุด คือประมาณ 2 นาที 30 นาที
ส่วนของ incremental build ใช้เวลาประมาณ 1 นาที 30 วินาที

ผลจาก Build from scratchScreen Shot 2559-01-09 at 11.59.29 AM

ผลจาก Incremental buildScreen Shot 2559-01-09 at 1.07.05 PM

จากรายงานที่ได้จาก profile พบว่าการ build ประกอบไปด้วย 3 ส่วน คือ

1. Configuration
โดยปกติมันจะเร็วมาก ๆ จะช้าก็คือ build.gradle มีความซับซ้อนมาก ๆ

2. Dependency Resolution
จะทำการตรวจสอบ และ download dependency ต่าง ๆ ที่ใช้งาน
ปกติจะดึงข้อมูลจาก cached เครื่องก่อน
ถ้าไม่มีจะทำการ download ผ่าน internet
ซึ่งเวลาการทำงานโดยรวมจะน้อยมาก ๆ

3. Task Execution !!
เป็นส่วนที่ใช้เวลาการทำงานสูงมาก ๆ
เพราะว่าใน gradle จะมีงานย่อย ๆ เยอะมาก
เช่น

  • Compiling
  • Dexing
  • Testing

จากเวลาการทำงาน หรือ build ของระบบงานตัวอย่าง
พบว่า Task Execution มันสูงที่สุด
นั่นคือ ปัญหา และ เป้าหมายที่ต้องการแก้ไขต่อไป !!!

มาเริ่มปรับปรุงเวลา build project กันเลย

อย่างแรกที่ให้ลองทำการปรับ
ใน gradle ทำการแนะนำมาดังนี้

This build could be faster, please consider using the Gradle Daemon: https://docs.gradle.org/2.8/userguide/gradle_daemon.html

นั่นคือเรื่องของ Gradle Daemon
พูดง่าย ๆ คือ มี background process หลาย ๆ ตัวมาช่วยทำงานนั่นเอง
เชิงทฤษฎีน่าจะ build ได้เร็วขึ้นสิ
แน่นอนว่าใช้ RAM เพิ่มขึ้นเช่นเดียวกัน !!
มาดูกันเลย

ทำการกำหนดค่าในไฟล์ gradle.properties
org.gradle.daemon=true

หรือใส่ใน command line ได้เลย
$./gradlew clean assembleDevDebug —daemon --profile

จากผลการทดสอบพบว่า

  • Build from scratch ใช้เวลาประมาณ 1 นาที 30 – 50 วินาที หรือลดลงไป 10-15%
  • Incremental build ใช้เวลาประมาณ 1 นาที 15 วินาที หรือลดลงไป 5% บางครั้งไม่ได้ช่วยอะไรเลย !!

แสดงผลการ Build from scratch ดังรูป
Screen Shot 2559-01-09 at 12.22.52 PM

และเรื่องอื่น ๆ ที่มักจะเขียน และ แนะนำมา เช่น

  • Parallel building สำหรับ multiple project
  • Configuration on demand สำหรับ multiple project
  • Offline mode
  • JVM arguments สำหรับ daemon

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

ข้อมูลเพิ่มเติม

ใน project มีการแยก code ออกเป็น module ย่อย ๆ
แต่มันกลับทำให้การ build ช้าลงไป
ซึ่งบางคนแก้ไขปัญหาด้วยการ build แบบ parallel

แต่สำหรับผมจะแยกออกไปเป็น library แทนนะครับ
ซึ่งลดเวลาการ build ไปได้เยอะกว่ามาก
จากที่ทดสอบ และ ใช้งานจริงพบว่าลดเวลาการ build ไป 50%
แต่ก็ยังทำงานช้าอยู่ดี เนื่องจากยังทำงานในระดับนาที !!

มาดู Task ใน gradle ที่ทำงานช้า ประกอบไปด้วย

  • transformClassesWithDex
  • compile
  • transformClassesWithMultidexlist
  • package
  • transformClassesWithJarMerging

จะลดเวลาการ build ของ task เหล่านี้อย่างไรกันดี ?

มาลองวิธีการต่อไปดีกว่า นั่นก็คือ เปลี่ยนค่า minSdkVersion เป็น 21

เป็นวิธีการเพิ่มประสิทธิภาพการ build ของ project ใหญ่ ๆ
แน่นอนว่า ต้องการใช้งาน multidex แน่นอน
และจะ build ช้ามาก ๆ ถ้าค่า minSdkVersion ต่ากว่า 21

เนื่องจากใน SDK 21 หรือ Lollipop ได้เปลี่ยนแปลง และ ปรับปรุงขั้นตอนการ build ใหม่
เพราะว่ามี runtime ตัวใหม่คือ ART (Android Runtime) มาแทน Dalvik
ซึ่งมีผลให้การ dexing ดีขึ้น
อ่านเพิ่มเติมเกี่ยวกับ multidex ได้ที่นี่ Android MultiDex 

ถ้าไม่เชื่อก็ลองเปลี่ยนกันหน่อยสิ
ดังนั้นลองเปลี่ยนค่า minSdkVersion เป็น 21 ก่อน
เพื่อความสะดวกจะกำหนดใน productFlavor dev
จากนั้นลอง build ดูใหม่ ได้ผลดังนี้

  • Build from scratch ใช้เวลาไม่ได้แตกต่างจากเดิมสักเท่าไร
  • Incremental build เป็นส่วนที่เวลาลดลงไปอย่างมาก เหลือไม่ถึง 30 วินาที หรือลดลงไป 50-70% กันเลย แต่ถ้ามี memory เหลือเยอะหน่อย จะใช้เวลา build 20 วินาที !!

โดยทดลองทั้งแบบ daemon และ non-daemon ซึ่งผลต่างกันพอสมควร
ซึ่งแนะนำให้ run แบบ daemon ไปเลย
ปล. ต้องเตรียม RAM ให้เยอะ ๆ หน่อยนะครับ

มาดูผลการทำงาน ซึ่งได้ผลเป็นที่น่าพอใจเลยนะ
Screen Shot 2559-01-09 at 4.02.50 PM

สิ่งที่เห็นได้ชัดเจนก็คือ compileDevDebugJavaWithJavac
นั่นคือการ compile code JAVA ด้วย Java Compiler นั่นเอง
ขั้นตอนนี้ใช้เวลาที่สูงสุด
ซึ่งน่าจะลดลงได้อีกนะ !!

ยังมีวิธีการการอื่น ๆ อีกนะ เช่น

  • ใช้ Jack และ Jill ซึ่ง Jack ใช้สำหรับการ compile code Java ไปยัง Dex format แต่ว่ามันไม่สนับสนุน Data Binding จึงไม่ได้ทำการทดสอบ
  • เพิ่ม Hardware เช่น RAM จะดีต่อการ build แน่นอน
  • แยก code ที่ไม่ค่อยเปลี่ยนแปลง ออกไปเป็น library หรือ dependency แทน จะได้ไม่ต้องเสียเวลามา build ใหม่ ดังนั้น Small is better นะครับ
  • ใช้ Buck สำหรับการ build แทนไปเลย

สุดท้าย ขอเน้นย้ำว่า

ก่อนจะทำการปรับปรุงอะไรก็ตาม
ควรที่จะทำการเก็บข้อมูลเริ่มต้น และ ทำ profiling ก่อนเสมอ
เพื่อทำให้เรารู้สถานะเริ่มต้นของระบบ
กับทำให้เรารู้ถึงปัญหา เพื่อจะทำการแก้ไขได้ตรงจุด
แต่ละปัญหา ย่อมใช้วิธีการแก้ไขที่ต่างกัน (No Silver Bullet)

ดังนั้น จงหาปัญหาของตัวเองให้เจอก่อนเสมอ
มิเช่นนั้น คุณจะไม่สามารถแก้ไขปัญหาได้
หรือถ้าได้ก็ไม่ตรงจุด !!

จากปัญหาที่ผมพบเจอ
สามารถลดเวลาการ build แบบ incremental
จาก 1 นาที 30 วินาที มาจนเหลือเวลาไม่ถึง 30 วินาที
ก็น่าจะทำให้เรามีกำลังใจในการ build มากขึ้น
รวมทั้งเป็นสิ่งบ่งบอกว่า เราแก้ไขปัญหามาถูกทางแล้วนะ
แต่ยังต้องลดเวลาลงไปอีก
เป้าหมายต่อไปคือ ต่ำกว่า 10 วินาที !!

Fast Feedback มันคือหัวใจสำคัญของการพัฒนา software นะ
ขอให้สนุกกับการพัฒนา Android application ครับ

Reference Websites
http://blog.octo.com/en/reduce-android-build-duration/
https://medium.com/@erikhellman/boosting-the-performance-for-gradle-in-your-android-projects-6d5f9e4580b6#.me0d9b7bm
http://kevinpelgrims.com/blog/2015/06/11/speeding-up-your-gradle-builds/
http://stackoverflow.com/questions/23367769/how-to-speed-up-android-studio-compilation-process
http://android-developers.blogspot.nl/2015/11/android-studio-20-preview.html
http://jimulabs.com/2014/10/speeding-gradle-builds/
https://www.timroes.de/2013/09/12/speed-up-gradle/
https://docs.gradle.org/2.8/userguide/gradle_daemon.html
http://gradle.org/why/high-performance-builds/
http://tools.android.com/tech-docs/new-build-system/tips