ปรับปรุงประสิทธิภาพของแอปด้วยโปรไฟล์พื้นฐาน

1. ก่อนเริ่มต้น

Codelab นี้จะแสดงวิธีการสร้างโปรไฟล์พื้นฐานเพื่อเพิ่มประสิทธิภาพให้กับแอปพลิเคชันของคุณ และวิธียืนยันประโยชน์ด้านประสิทธิภาพของการใช้โปรไฟล์พื้นฐาน

สิ่งที่คุณต้องมี

สิ่งที่คุณต้องทำ

  • ตั้งค่าโปรเจ็กต์เพื่อใช้เครื่องมือสร้างโปรไฟล์พื้นฐาน
  • สร้างโปรไฟล์พื้นฐานเพื่อเพิ่มประสิทธิภาพการเริ่มต้นและการเลื่อนแอป
  • ตรวจสอบประสิทธิภาพที่ได้รับด้วยไลบรารี Jetpack Macrobenchmark

สิ่งที่คุณจะได้เรียนรู้

  • โปรไฟล์พื้นฐานและวิธีที่โปรไฟล์เหล่านี้สามารถปรับปรุงประสิทธิภาพของแอป
  • วิธีสร้างโปรไฟล์พื้นฐาน
  • ประสิทธิภาพที่เพิ่มขึ้นของโปรไฟล์พื้นฐาน

2. การตั้งค่า

หากต้องการเริ่มต้นใช้งาน ให้โคลนที่เก็บ GitHub จากบรรทัดคำสั่งโดยใช้คำสั่งต่อไปนี้

$ git clone https://github.com/android/codelab-android-performance.git

หรือคุณจะดาวน์โหลดไฟล์ ZIP 2 ไฟล์ได้ดังนี้

เปิดโปรเจ็กต์ใน Android Studio

  1. ในหน้าต่างยินดีต้อนรับสู่ Android Studio ให้เลือก 61d0a4432ef6d396.png เปิดโปรเจ็กต์ที่มีอยู่
  2. เลือกโฟลเดอร์ [Download Location]/codelab-android-performance/baseline-profiles โปรดเลือกไดเรกทอรี baseline-profiles
  3. เมื่อ Android Studio นำเข้าโปรเจ็กต์ โปรดตรวจสอบว่าคุณสามารถเรียกใช้โมดูล app เพื่อสร้างแอปพลิเคชันตัวอย่างที่จะใช้งานในภายหลังได้

แอปตัวอย่าง

ใน Codelab นี้ คุณจะทำงานกับแอปพลิเคชัน JetSnack ตัวอย่างได้ ซึ่งเป็นแอปสั่งซื้อขนมเสมือนจริงที่ใช้ Jetpack Compose

ในการวัดประสิทธิภาพของแอปพลิเคชัน คุณต้องเข้าใจโครงสร้าง UI และลักษณะการทำงานของแอป เพื่อเข้าถึงองค์ประกอบ UI จากการเปรียบเทียบ เรียกใช้แอปและทำความคุ้นเคยกับหน้าจอพื้นฐานด้วยการสั่งขนม คุณไม่จำเป็นต้องทราบรายละเอียดเกี่ยวกับสถาปัตยกรรมของแอป

23633b02ac7ce1bc.png

3. โปรไฟล์พื้นฐานคืออะไร

โปรไฟล์พื้นฐานจะเพิ่มความเร็วในการดำเนินการกับโค้ดได้ประมาณ 30% นับจากการเปิดใช้ครั้งแรกโดยหลีกเลี่ยงการตีความและขั้นตอนการคอมไพล์แบบ Just-In-Time (JIT) สำหรับเส้นทางโค้ดที่รวมไว้ ด้วยการจัดส่งโปรไฟล์พื้นฐานในแอปหรือไลบรารี Android Runtime (ART) จึงสามารถเพิ่มประสิทธิภาพเส้นทางโค้ดที่รวมไว้ผ่านการคอมไพล์ Ahead of Time (AOT) ซึ่งเป็นการเพิ่มประสิทธิภาพสำหรับผู้ใช้ใหม่ทุกรายและในทุกๆ การอัปเดตแอป การเพิ่มประสิทธิภาพตามแนวทางโปรไฟล์ (PGO) นี้ช่วยให้แอปเพิ่มประสิทธิภาพการเริ่มต้น ลดความยุ่งยากในการโต้ตอบ และปรับปรุงประสิทธิภาพรันไทม์โดยรวมสำหรับผู้ใช้ปลายทางตั้งแต่การเปิดใช้ครั้งแรก

เมื่อใช้โปรไฟล์พื้นฐาน การโต้ตอบทั้งหมดของผู้ใช้ เช่น การเริ่มต้นแอป การไปยังหน้าจอต่างๆ หรือการเลื่อนผ่านเนื้อหา จะราบรื่นยิ่งขึ้นตั้งแต่ครั้งแรกที่เรียกใช้ การเพิ่มความเร็วและการตอบสนองของแอปจะทำให้ผู้ใช้ที่ใช้งานอยู่รายวันมากขึ้นและมีอัตราการเยี่ยมชมเฉลี่ยสูงขึ้น

โปรไฟล์พื้นฐานช่วยแนะแนวทางในการเพิ่มประสิทธิภาพนอกเหนือจากการเริ่มต้นแอป โดยแสดงการโต้ตอบของผู้ใช้ทั่วไป ซึ่งจะปรับปรุงรันไทม์ของแอปตั้งแต่การเปิดใช้ครั้งแรก การคอมไพล์ AOT แบบมีคำแนะนำไม่ได้ใช้อุปกรณ์ของผู้ใช้ และสามารถทำได้ 1 ครั้งต่อรุ่นบนเครื่องสำหรับการพัฒนาแทนที่จะใช้อุปกรณ์เคลื่อนที่ การจัดส่งรุ่นที่มีโปรไฟล์พื้นฐานจะทำให้การเพิ่มประสิทธิภาพแอปพร้อมใช้งานเร็วกว่าการใช้โปรไฟล์ Cloud เพียงอย่างเดียวเป็นอย่างมาก

เมื่อไม่ได้ใช้โปรไฟล์พื้นฐาน โค้ดของแอปทั้งหมดจะได้รับการคอมไพล์ JIT ในหน่วยความจำหลังจากตีความหรือลงในไฟล์ odex ในเบื้องหลังเมื่ออุปกรณ์ไม่มีการใช้งาน ผู้ใช้อาจได้รับประสบการณ์ที่ไม่ดีเมื่อเรียกใช้แอปหลังจากติดตั้งหรืออัปเดตแอปเป็นครั้งแรกก่อนที่จะมีการเพิ่มประสิทธิภาพเส้นทางใหม่

4. ตั้งค่าโมดูลตัวสร้างโปรไฟล์พื้นฐาน

คุณสามารถสร้างโปรไฟล์พื้นฐานด้วยคลาสทดสอบการใช้เครื่องมือที่จำเป็นต้องเพิ่มโมดูล Gradle ใหม่ในโปรเจ็กต์ของคุณ วิธีที่ง่ายที่สุดในการเพิ่มลงในโปรเจ็กต์คือการใช้วิซาร์ดโมดูล Android Studio ที่มาพร้อมกับ Android Studio Hedgehog ขึ้นไป

เปิดหน้าต่างวิซาร์ดโมดูลใหม่โดยคลิกขวาที่โปรเจ็กต์หรือโมดูลในแผงโปรเจ็กต์ แล้วเลือกใหม่ > โมดูล

232b04efef485e9c.png

จากหน้าต่างที่เปิดอยู่ ให้เลือกตัวสร้างโปรไฟล์พื้นฐานจากแผงเทมเพลต

b191fe07969e8c26.png

นอกเหนือจากพารามิเตอร์ทั่วไป เช่น ชื่อโมดูล ชื่อแพ็กเกจ ภาษา หรือภาษาการกำหนดค่าบิลด์แล้ว ยังมีอินพุต 2 รายการที่ต่างไปจากเดิมสำหรับโมดูลใหม่ ซึ่งได้แก่ แอปพลิเคชันเป้าหมายและใช้อุปกรณ์ที่มีการจัดการโดย Gradle

แอปพลิเคชันเป้าหมายคือโมดูลแอปที่ใช้ในการสร้างโปรไฟล์พื้นฐาน หากคุณมีโมดูลแอปมากกว่า 1 รายการในโปรเจ็กต์ ให้เลือกโมดูลที่ต้องการเรียกใช้โปรแกรมสร้าง

ช่องทำเครื่องหมายใช้อุปกรณ์ที่มีการจัดการจาก Gradle ตั้งค่าโมดูลให้เรียกใช้ตัวสร้างโปรไฟล์พื้นฐานในโปรแกรมจำลอง Android ที่มีการจัดการโดยอัตโนมัติ อ่านเพิ่มเติมเกี่ยวกับอุปกรณ์ที่มีการจัดการโดย Gradle ได้ในปรับขนาดการทดสอบด้วยอุปกรณ์ที่มีการจัดการของ Gradle หากยกเลิกการเลือก เครื่องปั่นไฟจะใช้อุปกรณ์ที่เชื่อมต่ออยู่

เมื่อคุณกำหนดรายละเอียดทั้งหมดเกี่ยวกับโมดูลใหม่แล้ว ให้คลิกเสร็จสิ้นเพื่อสร้างโมดูลต่อ

การเปลี่ยนแปลงที่ทำโดยวิซาร์ดโมดูล

วิซาร์ดโมดูลทำการเปลี่ยนแปลงหลายอย่างกับโครงการของคุณ

ระบบจะเพิ่มโมดูล Gradle ชื่อ baselineprofile หรือชื่อที่คุณเลือกในวิซาร์ด

โมดูลนี้ใช้ปลั๊กอิน com.android.test ซึ่งบอกให้ Gradle ไม่รวมปลั๊กอินนี้ในแอปพลิเคชันของคุณ ดังนั้นจึงมีได้เฉพาะโค้ดการทดสอบหรือการเปรียบเทียบเท่านั้น นอกจากนี้ยังใช้ปลั๊กอิน androidx.baselineprofile ซึ่งทำให้สร้างโปรไฟล์พื้นฐานได้โดยอัตโนมัติ

วิซาร์ดยังทำการเปลี่ยนแปลงโมดูลแอปพลิเคชันเป้าหมายที่คุณเลือกอีกด้วย กล่าวโดยละเอียดคือ ทรัพยากรดังกล่าวใช้ปลั๊กอิน androidx.baselineprofile, เพิ่มทรัพยากร Dependency ของ androidx.profileinstaller และเพิ่มทรัพยากร Dependency baselineProfile ไปยังโมดูล build.gradle(.kts) ที่สร้างขึ้นใหม่ ดังนี้

plugins {
  id("androidx.baselineprofile")
}

dependencies {
  // ...
  implementation("androidx.profileinstaller:profileinstaller:1.3.0")
  "baselineProfile"(project(mapOf("path" to ":baselineprofile")))
}

การเพิ่มทรัพยากร Dependency ของ androidx.profileinstaller จะช่วยให้คุณทําสิ่งต่อไปนี้ได้

  • ตรวจสอบประสิทธิภาพที่เพิ่มขึ้นของโปรไฟล์พื้นฐานที่สร้างขึ้นในพื้นที่
  • ใช้โปรไฟล์พื้นฐานใน Android 7 (API ระดับ 24) และ Android 8 (Api ระดับ 26) ซึ่งไม่รองรับโปรไฟล์ Cloud
  • ใช้โปรไฟล์พื้นฐานในอุปกรณ์ที่ไม่มีบริการ Google Play

ทรัพยากร Dependency baselineProfile(project(":baselineprofile")) ทําให้ Gradle ทราบว่าต้องใช้โปรไฟล์พื้นฐานที่สร้างขึ้นจากโมดูลใด

ตอนนี้คุณได้ตั้งค่าโปรเจ็กต์แล้ว ให้เขียนคลาสตัวสร้างโปรไฟล์พื้นฐาน

5. เขียนตัวสร้างโปรไฟล์พื้นฐาน

โดยปกติแล้ว คุณจะสร้างโปรไฟล์พื้นฐานสำหรับเส้นทางของผู้ใช้ทั่วไปในแอป

วิซาร์ดโมดูลจะสร้างคลาสการทดสอบ BaselineProfileGenerator พื้นฐานที่สามารถสร้างโปรไฟล์พื้นฐานสำหรับการเริ่มต้นแอปของคุณ และจะมีลักษณะดังต่อไปนี้

@RunWith(AndroidJUnit4::class)
@LargeTest
class BaselineProfileGenerator {

    @get:Rule
    val rule = BaselineProfileRule()

    @Test
    fun generate() {
        rule.collect("com.example.baselineprofiles_codelab") {
            // This block defines the app's critical user journey. This is where you
            // optimize for app startup. You can also navigate and scroll
            // through your most important UI.

            // Start default activity for your app.
            pressHome()
            startActivityAndWait()

            // TODO Write more interactions to optimize advanced journeys of your app.
            // For example:
            // 1. Wait until the content is asynchronously loaded.
            // 2. Scroll the feed content.
            // 3. Navigate to detail screen.

            // Check UiAutomator documentation for more information about how to interact with the app.
            // https://d.android.com/training/testing/other-components/ui-automator
        }
    }
}

ชั้นเรียนนี้ใช้กฎการทดสอบ BaselineProfileRule และมีวิธีทดสอบ 1 วิธีสำหรับการสร้างโปรไฟล์ จุดแรกเข้าสำหรับการสร้างโปรไฟล์คือฟังก์ชัน collect() โดยใช้พารามิเตอร์เพียง 2 ตัว ได้แก่

  • packageName: แพ็กเกจของแอป
  • profileBlock: พารามิเตอร์ lambda สุดท้าย

ใน lambda profileBlock คุณจะระบุการโต้ตอบที่ครอบคลุมเส้นทางของผู้ใช้ทั่วไปในแอป ไลบรารีจะเรียกใช้ profileBlock หลายครั้ง รวบรวมคลาสและฟังก์ชันที่เรียกใช้ และสร้างโปรไฟล์พื้นฐานในอุปกรณ์พร้อมกับโค้ดที่จะเพิ่มประสิทธิภาพ

โดยค่าเริ่มต้น คลาสตัวสร้างที่สร้างขึ้นจะมีการโต้ตอบเพื่อเริ่ม Activity เริ่มต้น และรอจนกว่าเฟรมแรกของแอปจะแสดงผลโดยใช้เมธอด startActivityAndWait()

ขยายเครื่องมือสร้างด้วยเส้นทางที่กำหนดเอง

นอกจากนี้ คุณยังเห็นว่าคลาสที่สร้างขึ้นมี TODO บางส่วนสำหรับเขียนการโต้ตอบเพิ่มเติมเพื่อเพิ่มประสิทธิภาพเส้นทางขั้นสูงของแอปด้วย ขอแนะนําให้ทําดังนี้เพื่อให้เพิ่มประสิทธิภาพได้นอกเหนือจากการเริ่มต้นแอป

ในแอปตัวอย่างของเรา คุณจะระบุเส้นทางเหล่านี้ได้โดยดำเนินการดังต่อไปนี้

  1. เริ่มต้นแอปพลิเคชัน คลาสที่สร้างขึ้นครอบคลุมส่วนนี้ไปแล้วบางส่วน
  2. รอจนกว่าเนื้อหาจะโหลดแบบไม่พร้อมกัน
  3. เลื่อนดูรายการอาหารว่าง
  4. ไปที่รายละเอียดอาหารว่าง

เปลี่ยนเครื่องมือสร้างให้มีฟังก์ชันสรุปที่ครอบคลุมเส้นทางทั่วไปในข้อมูลโค้ดต่อไปนี้

// ...
rule.collect("com.example.baselineprofiles_codelab") {
    // This block defines the app's critical user journey. This is where you
    // optimize for app startup. You can also navigate and scroll
    // through your most important UI.

    // Start default activity for your app.
    pressHome()
    startActivityAndWait()

    // TODO Write more interactions to optimize advanced journeys of your app.
    // For example:
    // 1. Wait until the content is asynchronously loaded.
    waitForAsyncContent()
    // 2. Scroll the feed content.
    scrollSnackListJourney()
    // 3. Navigate to detail screen.
    goToSnackDetailJourney()

    // Check UiAutomator documentation for more information about how to interact with the app.
    // https://d.android.com/training/testing/other-components/ui-automator
}
// ...

ต่อไป ให้เขียนการโต้ตอบของแต่ละเส้นทางที่กล่าวถึง คุณสามารถเขียนเป็นฟังก์ชันส่วนขยายของ MacrobenchmarkScope เพื่อให้มีสิทธิ์เข้าถึงพารามิเตอร์และฟังก์ชันที่มี การเขียนในลักษณะนี้ช่วยให้คุณนำการโต้ตอบกับตัวเปรียบเทียบมาใช้ซ้ำเพื่อยืนยันประสิทธิภาพที่ได้รับ

รอเนื้อหาแบบไม่พร้อมกัน

แอปจำนวนมากมีการโหลดแบบไม่พร้อมกันบางอย่างเมื่อเริ่มต้นแอป หรือที่เรียกว่าสถานะแสดงอย่างเต็มที่ ซึ่งจะบอกระบบเมื่อเนื้อหาโหลดและแสดงผล และผู้ใช้จะโต้ตอบกับเนื้อหาได้ รอสถานะในโปรแกรมสร้าง (waitForAsyncContent) ที่มีการโต้ตอบต่อไปนี้

  1. ค้นหารายการอาหารว่างในฟีด
  2. รอจนกว่าบางรายการในลิสต์นั้นจะปรากฏบนหน้าจอ
fun MacrobenchmarkScope.waitForAsyncContent() {
   device.wait(Until.hasObject(By.res("snack_list")), 5_000)
   val contentList = device.findObject(By.res("snack_list"))
   // Wait until a snack collection item within the list is rendered.
   contentList.wait(Until.hasObject(By.res("snack_collection")), 5_000)
}

เส้นทางรายการแบบเลื่อน

สําหรับเส้นทางรายการอาหารว่างแบบเลื่อน (scrollSnackListJourney) คุณติดตามการโต้ตอบต่อไปนี้ได้

  1. ค้นหาองค์ประกอบ UI ของรายการอาหารว่าง
  2. ตั้งค่าระยะขอบของท่าทางสัมผัสไม่ให้เรียกใช้การนำทางของระบบ
  3. เลื่อนรายการและรอจนกว่า UI จะดีขึ้น
fun MacrobenchmarkScope.scrollSnackListJourney() {
   val snackList = device.findObject(By.res("snack_list"))
   // Set gesture margin to avoid triggering gesture navigation.
   snackList.setGestureMargin(device.displayWidth / 5)
   snackList.fling(Direction.DOWN)
   device.waitForIdle()
}

ไปที่เส้นทางโดยละเอียด

เส้นทางสุดท้าย (goToSnackDetailJourney) นำการโต้ตอบต่อไปนี้ไปใช้

  1. ค้นหารายการอาหารว่างและขนมทั้งหมดที่คุณใช้ได้
  2. เลือกสินค้าจากรายการ
  3. คลิกรายการและรอจนกว่าหน้าจอรายละเอียดจะโหลด คุณใช้ประโยชน์จากข้อเท็จจริงที่ว่ารายการอาหารว่างไม่อยู่บนหน้าจออีกแล้ว
fun MacrobenchmarkScope.goToSnackDetailJourney() {
    val snackList = device.findObject(By.res("snack_list"))
    val snacks = snackList.findObjects(By.res("snack_item"))
    // Select snack from the list based on running iteration.
    val index = (iteration ?: 0) % snacks.size
    snacks[index].click()
    // Wait until the screen is gone = the detail is shown.
    device.wait(Until.gone(By.res("snack_list")), 5_000)
}

หลังจากที่คุณกำหนดการโต้ตอบทั้งหมดที่จำเป็นเพื่อให้ตัวสร้างโปรไฟล์พื้นฐานพร้อมใช้งาน คุณจะต้องกำหนดอุปกรณ์ที่ใช้

6. เตรียมอุปกรณ์ที่จะเรียกใช้เครื่องกำเนิดไฟฟ้า

หากต้องการสร้างโปรไฟล์พื้นฐาน เราขอแนะนำให้ใช้โปรแกรมจำลอง เช่น อุปกรณ์ที่มีการจัดการโดย Gradle หรืออุปกรณ์ที่ใช้ Android 13 (API 33) ขึ้นไป

คุณสามารถใช้อุปกรณ์ที่มีการจัดการของ Gradle เพื่อทำให้กระบวนการทำซ้ำได้และสร้างโปรไฟล์พื้นฐานโดยอัตโนมัติ อุปกรณ์ที่มีการจัดการของ Gradle ให้คุณเรียกใช้การทดสอบในโปรแกรมจำลองของ Android โดยไม่ต้องเปิดใช้งานและแยกอุปกรณ์ออกด้วยตนเอง ดูข้อมูลเพิ่มเติมเกี่ยวกับอุปกรณ์ที่มีการจัดการของ Gradle ได้ในปรับขนาดการทดสอบด้วยอุปกรณ์ที่มีการจัดการของ Gradle

หากต้องการกำหนดอุปกรณ์ที่มีการจัดการของ Gradle ให้เพิ่มคำจำกัดความลงในไฟล์ :baselineprofile โมดูล build.gradle.kts ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

android {
  // ...

  testOptions.managedDevices.devices {
    create<ManagedVirtualDevice>("pixel6Api31") {
        device = "Pixel 6"
        apiLevel = 31
        systemImageSource = "aosp"
    }
  } 
}

ในกรณีนี้ เราใช้ Android 11 (API ระดับ 31) และอิมเมจระบบ aosp มีสิทธิ์เข้าถึงแบบรูทได้

ถัดไป ให้กำหนดค่าปลั๊กอิน Baseline Profile Gradle ใช้อุปกรณ์ที่มีการจัดการโดย Gradle ที่กำหนด ในการดําเนินการดังกล่าว ให้เพิ่มชื่ออุปกรณ์ลงในพร็อพเพอร์ตี้ managedDevices และปิดใช้ useConnectedDevices ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

android {
  // ...
}

baselineProfile {
   managedDevices += "pixel6Api31"
   useConnectedDevices = false
}

dependencies {
  // ...
}

จากนั้นให้สร้างโปรไฟล์พื้นฐาน

7. สร้างโปรไฟล์พื้นฐาน

เมื่ออุปกรณ์พร้อมแล้ว คุณก็สร้างโปรไฟล์พื้นฐานได้ ปลั๊กอิน Baseline Profile Gradle จะสร้างงาน Gradle ขึ้น เพื่อทำให้ขั้นตอนทั้งหมดในการเรียกใช้คลาสทดสอบของโปรแกรมสร้างทำงานโดยอัตโนมัติ และนำโปรไฟล์พื้นฐานที่สร้างขึ้นไปใช้กับแอปของคุณ

วิซาร์ดโมดูลใหม่สร้างการกำหนดค่าการเรียกใช้เพื่อให้เรียกใช้งาน Gradle ที่มีพารามิเตอร์ที่จำเป็นทั้งหมดเพื่อเรียกใช้ได้อย่างรวดเร็วโดยไม่ต้องสลับระหว่างเทอร์มินัลและ Android Studio

หากต้องการเรียกใช้ ให้ค้นหาการกำหนดค่าการเรียกใช้ Generate Baseline Profile แล้วคลิกปุ่มเรียกใช้ 599be5a3531f863b.png

6911ecf1307a213f.png

งานจะเริ่มต้นอิมเมจโปรแกรมจำลองที่ระบุไว้ก่อนหน้านี้ เรียกใช้การโต้ตอบจากคลาสทดสอบ BaselineProfileGenerator หลายๆ ครั้ง จากนั้นแยกโปรแกรมจำลองออกแล้วส่งเอาต์พุตไปยัง Android Studio

เมื่อเครื่องมือสร้างเสร็จสมบูรณ์ ปลั๊กอิน Gradle จะวาง baseline-prof.txt ที่สร้างขึ้นลงในแอปพลิเคชันเป้าหมาย (โมดูล :app) ในโฟลเดอร์ src/release/generated/baselineProfile/ โดยอัตโนมัติ

fa0f52de5d2ce5e8.png

(ไม่บังคับ) เรียกใช้โปรแกรมสร้างจากบรรทัดคำสั่ง

หรือเรียกใช้โปรแกรมสร้างจากบรรทัดคำสั่งก็ได้ คุณสามารถใช้ประโยชน์จากงานที่สร้างโดยอุปกรณ์ที่มีการจัดการของ Gradle - :app:generateBaselineProfile คำสั่งนี้จะเรียกใช้การทดสอบทั้งหมดในโปรเจ็กต์ที่กำหนดโดยทรัพยากร Dependency baselineProfile(project(:baselineProfile)) เนื่องจากโมดูลยังมีการเปรียบเทียบสำหรับการยืนยันประสิทธิภาพที่เพิ่มขึ้นในภายหลัง การทดสอบดังกล่าวจึงล้มเหลวโดยมีคำเตือนเกี่ยวกับการเรียกใช้การเปรียบเทียบในโปรแกรมจำลอง

android
   .testInstrumentationRunnerArguments
   .androidx.benchmark.enabledRules=BaselineProfile

สำหรับกรณีนี้ คุณสามารถกรองตัวสร้างโปรไฟล์พื้นฐานทั้งหมดที่มีอาร์กิวเมนต์ตัวเรียกใช้การวัดต่อไปนี้ และข้ามการเปรียบเทียบทั้งหมด

คำสั่งทั้งหมดจะมีลักษณะดังนี้

./gradlew :app:generateBaselineProfile -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile

เผยแพร่แอปด้วยโปรไฟล์พื้นฐาน

เมื่อสร้างโปรไฟล์พื้นฐานและคัดลอกลงในซอร์สโค้ดของแอปแล้ว ให้สร้างแอปเวอร์ชันที่ใช้งานจริงตามปกติ คุณไม่จำเป็นต้องดำเนินการใดๆ เพิ่มเติมเพื่อแจกจ่ายโปรไฟล์พื้นฐานให้กับผู้ใช้ ปลั๊กอินจะเลือกโดยปลั๊กอิน Android Gradle ระหว่างบิลด์และรวมอยู่ใน AAB หรือ APK ต่อไป ให้อัปโหลดบิลด์ไปยัง Google Play

เมื่อผู้ใช้ติดตั้งแอปหรืออัปเดตแอปจากเวอร์ชันก่อนหน้า ระบบจะติดตั้งโปรไฟล์พื้นฐานด้วยเช่นกัน ซึ่งส่งผลให้เกิดประสิทธิภาพที่ดีขึ้นตั้งแต่การเรียกใช้แอปครั้งแรก

ขั้นตอนถัดไปจะแสดงวิธีตรวจสอบว่าประสิทธิภาพของแอปมีการปรับปรุงมากน้อยเพียงใดโดยใช้โปรไฟล์พื้นฐาน

8. (ไม่บังคับ) ปรับแต่งการสร้างโปรไฟล์พื้นฐาน

ปลั๊กอิน Gradle โปรไฟล์พื้นฐานมีตัวเลือกให้คุณปรับแต่งวิธีการสร้างโปรไฟล์ให้ตรงกับความต้องการเฉพาะของคุณ คุณเปลี่ยนลักษณะการทำงานได้ด้วยบล็อกการกำหนดค่า baselineProfile { } ในสคริปต์บิลด์

บล็อกการกำหนดค่าภายในโมดูล :baselineprofile จะส่งผลต่อวิธีเรียกใช้เครื่องมือสร้างที่สามารถเพิ่ม managedDevices และตัดสินใจว่าจะuseConnectedDevicesหรืออุปกรณ์ที่จัดการโดย Gradle

บล็อกการกำหนดค่าภายในโมดูลเป้าหมาย :app จะกำหนดว่าระบบจะบันทึกโปรไฟล์ไว้ที่ใดหรือสร้างขึ้นอย่างไร คุณสามารถเปลี่ยนพารามิเตอร์ต่อไปนี้ได้

  • automaticGenerationDuringBuild: หากเปิดใช้ คุณจะสร้างโปรไฟล์พื้นฐานได้เมื่อสร้างบิลด์เวอร์ชันที่ใช้งานจริง ซึ่งจะเป็นประโยชน์เมื่อสร้างบน CI ก่อนจัดส่งแอป
  • saveInSrc: ระบุว่าเก็บโปรไฟล์พื้นฐานที่สร้างขึ้นไว้ในโฟลเดอร์ src/ หรือไม่ หรือคุณจะเข้าถึงไฟล์จากโฟลเดอร์บิลด์ :baselineprofile ก็ได้
  • baselineProfileOutputDir: กำหนดตำแหน่งที่เก็บโปรไฟล์พื้นฐานที่สร้างขึ้น
  • mergeIntoMain: โดยค่าเริ่มต้น ระบบจะสร้างโปรไฟล์พื้นฐานตามตัวแปรของบิลด์ (เวอร์ชันผลิตภัณฑ์และประเภทบิลด์) หากต้องการรวมโปรไฟล์ทั้งหมดลงใน src/main คุณสามารถทำได้โดยเปิดใช้การตั้งค่าสถานะนี้
  • filter: คุณกรองคลาสหรือวิธีการที่จะรวมหรือยกเว้นจากโปรไฟล์พื้นฐานที่สร้างขึ้นได้ การดำเนินการนี้จะเป็นประโยชน์สำหรับนักพัฒนาไลบรารีที่ต้องการเฉพาะโค้ดจากไลบรารีที่รวมอยู่

9. ยืนยันการปรับปรุงประสิทธิภาพสตาร์ทอัพ

หลังจากสร้างโปรไฟล์พื้นฐานและเพิ่มลงในแอปแล้ว ให้ตรวจสอบว่าโปรไฟล์ดังกล่าวส่งผลต่อประสิทธิภาพของแอปตามที่ต้องการ

วิซาร์ดโมดูลใหม่จะสร้างคลาสการเปรียบเทียบชื่อ StartupBenchmarks ซึ่งมีการเปรียบเทียบเพื่อวัดเวลาเริ่มต้นของแอป และเปรียบเทียบกับเมื่อแอปใช้โปรไฟล์พื้นฐาน

ชั้นเรียนจะมีลักษณะดังนี้

@RunWith(AndroidJUnit4::class)
@LargeTest
class StartupBenchmarks {

    @get:Rule
    val rule = MacrobenchmarkRule()

    @Test
    fun startupCompilationNone() =
        benchmark(CompilationMode.None())

    @Test
    fun startupCompilationBaselineProfiles() =
        benchmark(CompilationMode.Partial(BaselineProfileMode.Require))

    private fun benchmark(compilationMode: CompilationMode) {
        rule.measureRepeated(
            packageName = "com.example.baselineprofiles_codelab",
            metrics = listOf(StartupTimingMetric()),
            compilationMode = compilationMode,
            startupMode = StartupMode.COLD,
            iterations = 10,
            setupBlock = {
                pressHome()
            },
            measureBlock = {
                startActivityAndWait()

                // TODO Add interactions to wait for when your app is fully drawn.
                // The app is fully drawn when Activity.reportFullyDrawn is called.
                // For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
                // from the AndroidX Activity library.

                // Check the UiAutomator documentation for more information on how to
                // interact with the app.
                // https://d.android.com/training/testing/other-components/ui-automator
            }
        )
    }
}

โดยใช้ MacrobenchmarkRule ที่สามารถเรียกใช้การเปรียบเทียบสำหรับแอปและรวบรวมเมตริกประสิทธิภาพได้ จุดแรกเข้าสำหรับการเขียนการเปรียบเทียบคือฟังก์ชัน measureRepeated จากกฎ

โดยต้องใช้พารามิเตอร์หลายรายการ ดังนี้

  • packageName:แอปพลิเคชันที่จะวัดผล
  • metrics: ประเภทข้อมูลที่คุณต้องการวัดในระหว่างการเปรียบเทียบ
  • iterations: จำนวนครั้งที่การเปรียบเทียบเกิดซ้ำ
  • startupMode: วิธีที่คุณต้องการให้แอปพลิเคชันของคุณเริ่มต้นเมื่อเริ่มการเปรียบเทียบ
  • setupBlock: การโต้ตอบกับแอปต้องเกิดขึ้นก่อนจึงจะวัดผลได้
  • measureBlock: การโต้ตอบกับแอปที่คุณต้องการวัดในระหว่างการเปรียบเทียบ

คลาสการทดสอบยังมีการทดสอบ 2 รายการด้วย ได้แก่ startupCompilationeNone() และ startupCompilationBaselineProfiles() ซึ่งเรียกใช้ฟังก์ชัน benchmark() ที่มี compilationMode ที่แตกต่างกัน

CompilationMode

พารามิเตอร์ CompilationMode จะกำหนดวิธีคอมไพล์แอปพลิเคชันเป็นโค้ดเครื่องล่วงหน้า ซึ่งมีตัวเลือกดังต่อไปนี้

  • DEFAULT: คอมไพล์แอปล่วงหน้าบางส่วนโดยใช้โปรไฟล์พื้นฐาน หากมี ใช้เมื่อไม่มีการใช้พารามิเตอร์ compilationMode
  • None(): รีเซ็ตสถานะการคอมไพล์แอปและไม่คอมไพล์แอปล่วงหน้า การคอมไพล์แบบทันท่วงที (JIT) ยังคงเปิดใช้ในระหว่างการใช้งานแอป
  • Partial(): คอมไพล์แอปไว้ล่วงหน้าโดยใช้โปรไฟล์พื้นฐานหรือการอุ่นเครื่อง หรือทั้งสองอย่าง
  • Full(): คอมไพล์โค้ดของแอปพลิเคชันทั้งหมดล่วงหน้า ตัวเลือกนี้เป็นตัวเลือกเดียวใน Android 6 (API 23) หรือต่ำกว่า

ถ้าต้องการเริ่มต้นเพิ่มประสิทธิภาพแอปพลิเคชัน คุณเลือกโหมดคอมไพล์ DEFAULT ได้ เนื่องจากประสิทธิภาพจะคล้ายกับเวลาที่ติดตั้งแอปจาก Google Play ถ้าคุณต้องการเปรียบเทียบประโยชน์ด้านประสิทธิภาพที่ได้รับจากโปรไฟล์พื้นฐาน คุณสามารถทำได้โดยการเปรียบเทียบผลลัพธ์ของโหมดการรวบรวม None และ Partial

แก้ไขการเปรียบเทียบเพื่อรอเนื้อหา

การเปรียบเทียบจะเขียนคล้ายกับตัวสร้างโปรไฟล์พื้นฐานด้วยการเขียนการโต้ตอบกับแอปของคุณ โดยค่าเริ่มต้น การเปรียบเทียบที่สร้างขึ้นจะรอให้มีการแสดงผลเฟรมแรกเท่านั้น ซึ่งคล้ายกับการทำงานของ BaselineProfileGenerator เราจึงแนะนำให้ปรับปรุงเพื่อรอเนื้อหาแบบไม่พร้อมกัน

คุณสามารถทำได้โดยนำฟังก์ชันของส่วนขยายที่คุณเขียนไว้สำหรับโปรแกรมสร้างมาใช้ใหม่ เนื่องจากการเปรียบเทียบนี้จะบันทึกช่วงเวลาเริ่มต้น (เมื่อใช้ StartupTimingMetric()) เราจึงขอแนะนําให้คุณใส่เฉพาะรอเนื้อหาแบบไม่พร้อมกันที่นี่ แล้วเขียนการเปรียบเทียบแยกต่างหากสำหรับเส้นทางอื่นๆ ของผู้ใช้ที่กำหนดไว้ในเครื่องมือสร้าง

// ...
measureBlock = {
   startActivityAndWait()

   // The app is fully drawn when Activity.reportFullyDrawn is called.
   // For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
   // from the AndroidX Activity library.
   waitForAsyncContent() // <------- Added to wait for async content.

   // Check the UiAutomator documentation for more information on how to
   // interact with the app.
   // https://d.android.com/training/testing/other-components/ui-automator
}

ทำการเปรียบเทียบ

คุณสามารถทำการเปรียบเทียบได้ในแบบเดียวกับที่ทำการทดสอบแบบมีเครื่องวัด คุณจะเรียกใช้ฟังก์ชันทดสอบ หรือเรียกใช้ทั้งชั้นเรียนก็ได้ โดยให้มีไอคอนรางน้ำอยู่ข้างๆ

587b04d1a76d1e9d.png

โปรดเลือกอุปกรณ์จริง เนื่องจากการเรียกใช้การเปรียบเทียบในโปรแกรมจำลอง Android ล้มเหลวขณะรันไทม์ โดยมีคำเตือนว่าการเปรียบเทียบอาจให้ผลลัพธ์ที่ไม่ถูกต้อง แม้ว่าในทางเทคนิคคุณจะเรียกใช้ไฟล์ดังกล่าวบนโปรแกรมจำลองได้ แต่คุณกำลังวัดประสิทธิภาพของเครื่องโฮสต์อยู่ หากมีภาระงานหนักเกินไป เกณฑ์การเปรียบเทียบของคุณจะช้าลงและตรงกันข้าม

94e0da86b6f399d5.png

เมื่อคุณเรียกใช้การเปรียบเทียบ ระบบจะสร้างแอปของคุณขึ้นมาใหม่แล้วจึงเรียกใช้การเปรียบเทียบ การเปรียบเทียบจะเริ่มต้น หยุด และแม้แต่ติดตั้งแอปของคุณอีกครั้งหลายครั้งโดยอิงตามiterationsที่คุณกำหนดไว้

หลังจากการเปรียบเทียบเสร็จสมบูรณ์ คุณจะดูเวลาได้ในเอาต์พุต Android Studio ดังที่แสดงในภาพหน้าจอต่อไปนี้

282f90d5f6ff5196.png

จากภาพหน้าจอ คุณจะเห็นว่าเวลาเริ่มต้นของแอปแตกต่างกันใน CompilationMode แต่ละรายการ ค่ามัธยฐานจะแสดงในตารางต่อไปนี้

timeToInitialDisplay [มิลลิวินาที]

timeToFullDisplay [มิลลิวินาที]

ไม่มี

202.2

818.8

BaselineProfiles

193.7

637.9

การปรับปรุง

4%

28%

ความแตกต่างระหว่างโหมดการรวบรวมสำหรับ timeToFullDisplay คือ 180 มิลลิวินาที ซึ่งดีขึ้นประมาณ 28% เพียงแค่มีโปรไฟล์พื้นฐานเท่านั้น CompilationNone ทำงานได้แย่ลงเนื่องจากอุปกรณ์มีการคอมไพล์ JIT บ่อยที่สุดในช่วงที่แอปเริ่มทำงาน CompilationBaselineProfiles มีประสิทธิภาพดีกว่าเนื่องจากการคอมไพล์บางส่วนโดยใช้โปรไฟล์พื้นฐาน AOT จะรวบรวมโค้ดที่ผู้ใช้มีแนวโน้มจะใช้มากที่สุด และไม่ทำการคอมไพล์โค้ดที่ไม่สำคัญไว้ล่วงหน้าเพื่อไม่ต้องโหลดโดยทันที

10. (ไม่บังคับ) ตรวจสอบการปรับปรุงประสิทธิภาพการเลื่อน

เช่นเดียวกับขั้นตอนก่อนหน้า คุณจะวัดและตรวจสอบประสิทธิภาพในการเลื่อนได้ ขั้นแรก ให้สร้างคลาสการทดสอบ ScrollBenchmarks ที่มีกฎการเปรียบเทียบและวิธีทดสอบ 2 วิธีที่ใช้โหมดการรวบรวมที่ต่างกัน ดังนี้

@LargeTest
@RunWith(AndroidJUnit4::class)
class ScrollBenchmarks {

   @get:Rule
   val rule = MacrobenchmarkRule()

   @Test
   fun scrollCompilationNone() = scroll(CompilationMode.None())

   @Test
   fun scrollCompilationBaselineProfiles() = scroll(CompilationMode.Partial())

   private fun scroll(compilationMode: CompilationMode) {
       // TODO implement
   }
}

จากภายในเมธอด scroll ให้ใช้ฟังก์ชัน measureRepeated กับพารามิเตอร์ที่ต้องระบุ สำหรับพารามิเตอร์ metrics ให้ใช้ FrameTimingMetric ซึ่งจะวัดระยะเวลาที่ใช้ในการสร้างเฟรม UI ดังนี้

private fun scroll(compilationMode: CompilationMode) {
   rule.measureRepeated(
       packageName = "com.example.baselineprofiles_codelab",
       metrics = listOf(FrameTimingMetric()),
       compilationMode = compilationMode,
       startupMode = StartupMode.WARM,
       iterations = 10,
       setupBlock = {
           // TODO implement
       },
       measureBlock = {
           // TODO implement
       }
   )
}

ครั้งนี้คุณต้องแบ่งการโต้ตอบระหว่าง setupBlock กับ measureBlock ให้มากขึ้นเพื่อวัดระยะเวลาของเฟรมระหว่างเลย์เอาต์แรกและเลื่อนเนื้อหาเท่านั้น ดังนั้น ให้วางฟังก์ชันที่เริ่มต้นหน้าจอเริ่มต้นไว้ที่ setupBlock และฟังก์ชันของส่วนขยายที่สร้างไว้แล้ว waitForAsyncContent() และ scrollSnackListJourney() ลงใน measureBlock ดังนี้

private fun scroll(compilationMode: CompilationMode) {
   rule.measureRepeated(
       packageName = "com.example.baselineprofiles_codelab",
       metrics = listOf(FrameTimingMetric()),
       compilationMode = compilationMode,
       startupMode = StartupMode.WARM,
       iterations = 10,
       setupBlock = {
           pressHome()
           startActivityAndWait()
       },
       measureBlock = {
           waitForAsyncContent()
           scrollSnackListJourney()
       }
   )
}

เมื่อการเปรียบเทียบพร้อมแล้ว คุณสามารถเรียกใช้การเปรียบเทียบก่อนหน้าเพื่อให้ได้ผลลัพธ์ดังที่แสดงในภาพหน้าจอต่อไปนี้

84aa99247226fc3a.png

FrameTimingMetric จะแสดงระยะเวลาของเฟรมเป็นมิลลิวินาที (frameDurationCpuMs) ในเปอร์เซ็นไทล์ที่ 50, 90, 95 และ 99 ใน Android 12 (API ระดับ 31) ขึ้นไป ระบบจะแสดงระยะเวลาที่เฟรมเกินขีดจำกัด (frameOverrunMs) ด้วย ค่าอาจเป็นลบ ซึ่งหมายความว่ามีเวลาเหลือในการสร้างเฟรม

จากผลลัพธ์ คุณจะเห็นว่า CompilationBaselineProfiles มีระยะเวลาโดยเฉลี่ยของเฟรมที่สั้นลง 2 มิลลิวินาที ซึ่งผู้ใช้อาจมองไม่เห็น อย่างไรก็ตาม สำหรับเปอร์เซ็นไทล์อื่นๆ ผลลัพธ์จะเห็นได้ชัดกว่า สำหรับ P99 ความแตกต่างคือ 43.5 มิลลิวินาที ซึ่งเท่ากับเฟรมที่ข้ามมากกว่า 3 เฟรมในอุปกรณ์ที่ทำงานที่ 90 FPS เช่น สำหรับ Pixel 6 จะใช้เวลา 1, 000 มิลลิวินาที / 90 FPS = เวลาสูงสุดประมาณ 11 มิลลิวินาทีในการแสดงภาพเฟรม

11. ขอแสดงความยินดี

ขอแสดงความยินดี คุณดำเนินการ Codelab นี้เสร็จแล้ว และปรับปรุงประสิทธิภาพของแอปด้วยโปรไฟล์พื้นฐาน

แหล่งข้อมูลเพิ่มเติม

โปรดดูแหล่งข้อมูลเพิ่มเติมต่อไปนี้

เอกสารอ้างอิง