目錄
參考
https://docs.gradle.org/current/userguide/intro_multi_project_builds.html
https://guides.gradle.org/performance/
1.構建績效指南
是否需要更快的Gradle構建?在此處註冊以參加我們的構建緩存培訓課程,以瞭解Gradle Enterprise如何將構建速度提高多達90%。
構建性能對於您的生產力至關重要。完成構建所需的時間越長,您從開發流程中退出的可能性就越大。最重要的是,由於您每天都要運行該構建多次,因此即使是很少的等待時間也可能導致嚴重的中斷。在CI上運行的構建也是如此:它們越快,您對新問題的反應就越快,您進行創新實驗的能力也就越大。
所有這些都意味着值得花費一些時間和精力來使構建速度儘可能快。本指南提供了幾種途徑,您可以探索這些途徑來加快構建速度,並提供大量詳細信息,說明哪些因素會降低構建性能以及原因。
1.1.構建掃描
構建掃描是運行構建時發生的事情的持久且可共享的記錄。通過構建掃描,您可以獲得有關構建的深刻見解,以識別和修復性能瓶頸。
如果您使用的是Gradle 4.3+,則可以使用--scan
命令行選項(例如gradle build --scan
)輕鬆創建構建掃描。對於較早的Gradle版本,請參閱《Build Scan Plugin用戶手冊》,以瞭解如何啓用構建掃描。
Gradle在構建執行結束時顯示可進行構建掃描的URL:
圖1.構建輸出末尾的構建掃描鏈接
接下來,該指南將介紹一些快速改進,這些改進可以提高構建的性能。之後,將更深入地研究使用構建掃描對構建進行概要分析。
1.2.輕鬆改進
有關性能調優的指南通常從概要分析開始,而過早優化則是萬惡之源。剖析絕對重要,指南稍後將進行討論,但是您可以做一些事情,一觸即發,它將影響您所有的構建。
1.2.1.使用最新的Gradle和JVM版本
Gradle團隊不斷致力於改善Gradle構建各個方面的性能。如果您使用的是Gradle的舊版本,則會錯過這項工作的好處。始終跟上Gradle版本升級。這樣做是低風險的,因爲Gradle團隊可確保Gradle的次要版本之間向後兼容。保持最新狀態也使過渡到下一個主要版本更加容易,因爲您會收到早期棄用警告。
升級到主要版本通常很容易。在這些邊界上,只有以前不推薦使用的API和不推薦使用的行爲才成爲失敗。因此,請務必修復這些棄用警告!如果它們是由第三方插件引起的,請立即告知作者,因此當下一個主要版本發佈時,您不會受到阻礙。
當Gradle在JVM上運行時,後者性能的提高通常會使Gradle受益。因此,您應該考慮使用JVM的最新主要版本運行Gradle。
1.2.2.並行執行
大多數構建都包含一個以上的項目,其中一些項目通常彼此獨立。然而,不管項目結構如何,默認情況下,Gradle一次只能運行一項任務(這將很快得到改善)。通過使用該--parallel
開關,您可以強制Gradle並行執行任務,只要這些任務位於不同的項目中即可。
啓用並行構建後,您可能會看到構建時間的巨大改進。這些改進的程度取決於您的項目結構以及它們之間有多少依賴關係。例如,執行時間由單個項目控制的構建根本不會帶來太多好處。或者,由於項目間存在大量依存關係,因此幾乎沒有可以並行執行的任務。但是大多數多項目構建應該看到有價值的構建時間增長。
並行構建要求在執行時將項目解耦,即,不同項目中的任務不得修改共享狀態。 廣泛使用
--parallel
前,請先閱讀《用戶手冊》中有關該主題的更多信息。 還請注意,4.0之前的Gradle版本可能會並行運行clean和build任務,從而導致失敗。 在這些舊版本上,最好單獨調用clean。
您還可以通過將以下設置添加到項目的gradle.properties文件中,將並行構建作爲項目的默認設置:
gradle.properties
org.gradle.parallel=true
構建掃描使您可以直觀地看到任務執行的時間表,並快速瞭解當前的並行度,從而可以識別和消除瓶頸。
例如,在以下示例構建中,您可以在構建的開始和結束時看到長期運行的任務,它們是唯一要執行的任務:
圖2.並行執行中的瓶頸
調整構建配置以儘早並並行運行兩個緩慢的任務,將整個構建時間從8秒減少到5秒:
圖3.優化的並行執行
那就是快速勝利的終點。從現在開始,提高構建性能將需要一些肘部潤滑脂。首先,也許是最重要的一步:找出構建中哪些緩慢,爲什麼?
1.3.使用構建掃描進行性能分析
在對構建進行概要分析的情況下使用構建掃描時,在診斷的早期階段主要關注的領域是性能頁面。要到達那裏,請在左側導航菜單中單擊“Performance”,或單擊構建掃描主頁的以下屏幕截圖中突出顯示的鏈接:
圖4.構建掃描主頁上的Performance頁面鏈接
性能頁面爲您提供了構建不同階段完成所需時間的細分。從下面的屏幕截圖中可以看到,您將看到Gradle啓動,配置構建項目,解決依賴關係以及執行任務所花費的時間。您還將獲得有關環境屬性的詳細信息,例如是否使用了守護程序。
圖5.構建掃描性能頁面
除了構建執行的頂級性能摘要之外,您還可以在性能頁面的不同選項卡中進一步深入研究影響構建性能的各個方面。
1.4.配置(Configuration)
正如用戶指南在構建生命週期一章中所述,Gradle構建經歷三個階段:初始化(initialization),配置(configuration)和執行(execution)。 這裏要了解的重要一點是,無論要運行哪些任務,配置代碼始終會執行。 這意味着在配置過程中執行的所有昂貴工作都會降低每次調用的速度,即使是像gradle help
和gradle tasks
這樣的簡單調用也是如此。
在上面的構建掃描性能頁面中,您可以看到構建配置耗時超過16秒。 單擊頁面頂部的“Configuration”選項卡將把此階段分解爲各個組成部分,以瞭解造成任何緩慢的原因。
圖6.構建掃描配置分解
在這裏,您可以查看應用到項目的腳本和插件,這些腳本和插件的應用時間以降序排列。最慢的插件和腳本應用程序是優化的最佳選擇,您可以在列表中進一步深入研究。例如,腳本script-b.gradle
只應用了一次,但是卻花了3秒鐘-您可以擴展該行,以查看將其應用於構建的方式和位置。
圖7.顯示script-b.gradle在構建中的應用
您可以看到項目:app1
從該項目build.gradle
文件的內部一次應用了此腳本。
接下來的幾個小節將介紹有助於縮短配置時間並解釋其工作原理的技術。
1.4.1.明智地應用插件
您應用於項目的每個插件和腳本都會增加總體配置時間。一些插件比其他插件具有更大的影響。這並不意味着您應該避免使用插件,但應注意僅在需要的地方應用它們。例如,很容易通過對插件適用於所有項目allprojects {}
或subprojects {}
即使不是每一個項目都需要它們。
在上面的構建掃描示例中,您可以看到該腳本script-a.gradle
已應用於構建中的3個項目:
圖8.顯示script-a.gradle在構建中的應用
該腳本需要1秒鐘才能運行,並且由於它已從根構建腳本應用於3個項目,因此在配置階段引入了3秒鐘的延遲。
理想情況下,插件和腳本不應花費大量的配置時間。如果這樣做,重點應該放在改進它們上。儘管如此,在具有許多模塊和大量配置時間的項目中,您應該花一些時間來確定所有具有顯着影響的插件。
1.4.2.避免昂貴或阻礙工作
如您所見,您將希望避免在配置階段進行耗時的工作,但有時它可能會在不明顯的地方潛入您的構建中。如果代碼在構建文件中,則在配置過程中加密數據或調用遠程服務時通常很清楚。但是類似的邏輯通常在插件和自定義任務類中找到。插件的apply()
方法或任務的構造函數中任何昂貴的工作都應該是一個危險信號。最常見且不太明顯的錯誤是在配置時解決依賴關係,這將在下面的單獨章節中介紹。
1.4.3.靜態編譯任務和插件
如果您的構建邏輯由使用靜態編譯的JVM語言(例如Java或Kotlin)編寫的插件以及使用Gradle Kotlin DSL編寫的構建腳本組成,那麼您可以跳過此步驟並繼續進行下一部分。
插件和偶爾的任務在配置階段執行工作。這些通常用Groovy編寫,因爲其語法簡潔,對JDK的API擴展以及使用閉包的函數方法。但是,重要的是要記住,動態Groovy中與方法調用相關的開銷很小。當您在許多項目中重複進行許多方法調用時,成本可能會增加。
可以在Groovy類上通過@CompileStatic
使用(可能的話)或以靜態編譯語言(例如Java)編寫這些類來降低成本。這僅適用於您公開發布的大型項目或插件(因爲其他用戶可能將其應用於大型項目)。如果確實需要隨時使用動態Groovy,則只需使用@CompileDynamic
相關方法即可。
注意:在構建腳本中使用的DSL很大程度上依賴於Groovy的動態功能,因此,如果要在插件中使用靜態編譯,則必須切換到更傳統的類似於Java的語法。例如,要創建一個新的複製任務,您將使用如下代碼:
src/main/groovy/MyPlugin.groovy
project.tasks.create('copyFiles', Copy) { Task t ->
t.into "${project.buildDir}/output"
t.from project.configurations.getByName('compile')
}
您可以看到該示例如何使用create()
和getByName()
方法,這些方法在所有Gradle“域對象容器”上都可用,例如任務(tasks),配置(configurations),依賴項(dependencies),擴展(extensions)等。某些集合具有專用的類型(TaskContainer是
其中之一)具有有用的額外方法。就像create()
上面採用任務類型的方法一樣。
如果您決定使用靜態編譯,則由於類型,屬性和方法無法識別,使用IDE可以快速顯示錯誤。您還將獲得自動完成功能,這總是很方便的。
1.5.依賴解析
軟件項目依靠依賴關係解析來簡化第三方庫和其他依賴關係到構建中的集成。這確實是有代價的,因爲Gradle必須聯繫遠程服務器以查找這些依賴項,並在必要時下載它們。高級緩存有助於極大地加快處理速度,但是您仍然需要注意接下來要討論的一些陷阱。
1.5.1.最小化動態和快照版本
動態版本(例如“2. +”)和快照(或更改)版本會強制Gradle與遠程存儲庫聯繫,以查找是否有新版本或快照可用。默認情況下,Gradle只會每24小時執行一次檢查,但是可以更改。在你構建文件和初始化腳本的時候,注意cacheDynamicVersionsFor
和cacheChangingModulesFor,
以防它們設置爲非常短的時間或完全禁用。否則,您可能會譴責構建用戶使用比正常情況更慢的構建,而不是每天進行一次比正常情況更慢的構建。
您可以通過構建掃描找到具有動態版本的所有依賴項:
圖9.查找具有動態版本的依賴項
您可能可以使用固定版本,例如“1.2”和“3.0.3.GA”,在這種情況下,Gradle將始終使用緩存的版本。但是,如果您需要使用動態版本和快照版本,請確保調整緩存設置以最好地滿足您的需求。
1.5.2.在配置時不要解決依賴關係
無論是在I / O還是計算方面,依賴性解析都是一個昂貴的過程。Gradle通過明智的緩存減少了(並在某些情況下消除了)所需的網絡流量,但仍然需要做一些工作。爲什麼這很重要?因爲如果在配置階段觸發依賴項解析,那麼您將對運行的每個構建都增加一個損失。
要回答的關鍵問題是什麼觸發依賴關係解決?最常見的原因是評估組成配置的文件。這通常是完成任務的工作,因爲在準備好執行任務操作中的文件之前,通常不需要文件。但是,假設您正在進行一些調試,並且想要顯示構成配置的文件。實現此目的的一種方法是注入print語句:
Groovy
build.gradle
task copyFiles(type: Copy) {
println ">> Compilation deps: ${configurations.compile.files}"
into "$buildDir/output"
from configurations.compile
}
Kotlin
build.gradle.kts
tasks.create<Copy>("copyFiles") {
println(">> Compilation deps: ${configurations.compile.files}")
into("$buildDir/output")
from(configurations.compile)
}
該files
屬性將強制Gradle解析依賴關係,在此示例中,此過程在配置階段發生。現在,每次運行構建時,無論執行什麼任務,都會從該配置的依賴項解決方案獲得性能上的損失。最好將其添加到doFirst()
操作中。
Groovy
build.gradle
task copyFiles(type: Copy) {
into "$buildDir/output"
from configurations.compile
doFirst {
println ">> Compilation deps: ${configurations.compile.files}"
}
}
Kotlin
build.gradle.kts
tasks.create<Copy>("copyFiles") {
into("$buildDir/output")
from(configurations.compile)
doFirst {
println(">> Compilation deps: ${configurations.compile.files}")
}
}
請注意,from()
聲明不會解析依賴關係,因爲您將依賴關係配置本身用作參數,而不是其文件。該Copy
任務處理配置本身的任務執行,而這正是你想要的東西時的分辨率。
構建掃描的性能頁面上的“依賴關係解決方案(Dependency resolution)”選項卡明確顯示瞭如何在項目配置和任務執行之間分配依賴關係解決時間:
圖10.配置時的依賴關係解析
在這裏,您可以快速確定導致此特定性能問題的原因。在“項目配置(project configuration" )”期間解決依賴關係所花費的時間 應爲0秒,並且此示例顯示構建在生命週期的早期解決依賴關係。同樣在“性能(Performance)”頁面上還有“設置和建議(Settings and suggestions)”選項卡,該選項卡將向您顯示在項目配置過程中正在解決哪些依賴項。
如果使用的是探查器,則可以通過運行以下命令來獲得類似(但不太詳細)的信息:
$ gradle --profile help
1.5.3.避免不必要和未使用的依賴項
有時您會遇到僅使用第三方庫中一個或兩個方法或類的情況。發生這種情況時,您應該認真考慮在項目中自己實現所需的代碼,或者如果可以的話,可以從開源庫中複製所需的代碼。請記住,管理第三方庫及其可傳遞依賴項不會增加項目維護和構建時間的成本。
要注意的另一件事是存在未使用的依賴項。代碼重構後,當第三方庫停止使用但未從依賴項列表中刪除時,很容易發生這種情況。您可以使用Gradle Lint插件來識別此類依賴項。
1.5.4.最小化倉庫數量
當Gradle嘗試解決依賴關係時,它將按照聲明它們的順序搜索每個存儲庫,直到找到該依賴關係爲止。通常,這意味着您要先聲明承載最大數量依賴關係的存儲庫,以便在大多數情況下僅搜索該存儲庫。您還應將已聲明的存儲庫數量限制爲最小可用數量,以使構建正常工作。
如果您使用的是自定義存儲庫服務器,則可用的一種技術是創建將多個實際存儲庫聚集在一起的虛擬存儲庫。然後,您可以只將該存儲庫添加到您的構建文件中,從而進一步減少Gradle在依賴性解析期間發送的HTTP請求的數量。
1.5.5.注意自定義依賴項解析邏輯
依賴性解析是一個很難解決的問題,要使其表現良好,只會增加挑戰。但是,Gradle仍然需要允許用戶以最適合他們的方式對依賴關係解析進行建模。這就是爲什麼它具有功能強大的API來自定義依賴項解析如何工作的原因。
簡單的自定義(例如,強制使用特定版本的依賴項或將一個依賴項替換爲另一種依賴項)不會對依賴項解決時間產生重大影響。但是,例如,如果自定義邏輯涉及下載和解析額外的POM,則影響可能很大。
您應該使用構建掃描或概要文件報告來檢查構建中具有的任何自定義依賴關係解析邏輯不會對依賴關係解析時間產生重大不利影響。請注意,這可能是您自己編寫的自定義邏輯,也可能是您正在使用的插件的一部分。
1.5.6.識別緩慢或意外的依賴項下載
依賴項下載速度慢(可能是由於Internet連接速度慢,存儲庫服務器過載或類似原因引起的)會影響整體構建性能。構建掃描在“性能(Performance)”頁面上提供了“網絡活動(Network Activity)”選項卡,其中列出了有用的信息,例如花費時間下載依賴關係,整個構建中依賴關係下載的總傳輸率以及按下載時間排序的下載列表。
在這裏,您可以看到兩次緩慢的依賴項下載,耗時20和40秒,並降低了構建的整體性能:
圖11.識別慢速依賴項下載
您還可以檢查下載列表,以確保在構建執行期間沒有意外的依賴項下載。例如,您可能會看到由使用動態版本的依賴項引起的意外下載。
1.6.任務執行
最快的任務是不執行的任務。如果您找到了跳過不需要運行的任務的方法,那麼最終將獲得更快的總體構建速度。本節將討論在Gradle構建中實現避免任務的幾種方法。
1.6.1.不同的人,不同的體形
將構建視爲全包或全包似乎很常見。每個用戶必須學習由構建定義的相同任務集。在許多情況下,這沒有任何意義。想象一下,您同時擁有前端和後端開發人員:他們是否希望從構建中獲得相同的東西?當然不是,特別是如果一側是HTML,CSS和Javascript,而另一側是Java和servlet。
重要的是,單個任務圖將爲構建提供基礎,以確保一致性。但是您不需要將整個任務圖公開給所有人。取而代之的是,根據在任務圖上形成受限視圖的任務集來思考,每個視圖都針對特定的用戶組。前端開發人員是否需要運行服務器端單元測試?不,因此,將成本強加給那些用戶沒有意義。
考慮到這一點,請考慮每個不同的用戶組所需的不同工作流程,並嘗試確保他們擁有適當的“視圖”,而沒有執行不必要的任務。Gradle有幾種方法可以幫助您實現這一目標:
-
將任務分配給適當的組
-
創建有用的彙總任務(不執行任何操作,僅依賴於一組其他任務的任務,例如
assemble
) -
通過
gradle.taskGraph.whenReady()
和其他方式推遲配置,因此只有在必要時纔可以執行驗證
肯定需要一些努力和時間上的投入來制定合適的構建視圖,但要考慮用戶多久運行一次構建。如果可以每天節省用戶時間,那麼肯定值得投資。
1.6.2.增量構建
您可以避免執行任務,即使用戶需要執行任務也是如此。如果自上次運行以來任務的輸入或輸出均未更改,則Gradle將不會再次運行它。
增量構建是Gradle賦予此功能的名稱,它檢查輸入和輸出以確定是否需要再次運行任務。Gradle提供的大多數任務都參與增量構建,因爲它們是通過這種方式定義的。您還可以按照用戶指南中的說明,將自己的任務與增量構建集成在一起。基本思想是標記任務的屬性,這些屬性會影響任務是否需要運行。您可以在用戶指南中瞭解更多信息。
您可以通過在構建掃描中查看時間軸視圖來輕鬆地確定參與增量構建的優秀候選人,並瞭解爲什麼任務沒有達到您期望的最新狀態:
圖12.時間線視圖可以幫助進行增量構建檢查
如您在上面的構建掃描中所看到的,該任務不是最新的,因爲其輸入之一“時間戳” 已更改,並且迫使該任務重新運行。
還可以按照最長持續時間先對任務進行排序,從而輕鬆挑選最慢的任務。選擇最慢的自定義任務,使其參與增量構建,然後再次進行測量並重復。
1.6.3.緩存任務輸出
基於任務的先前執行,增量構建在本地工作。Gradle還可以將任務輸出存儲在構建緩存中,並在稍後將執行具有相同輸入的相同任務時檢索它們。您可以使用本地緩存在計算機上重用任務輸出。這有助於減少切換分支時的構建時間。
也可以使用共享的構建緩存服務,例如Gradle Enterprise提供的服務。共享緩存可以通過重用已在其他位置生成的輸出來減少您需要執行的任務數量。這可以顯着減少CI和開發人員構建的構建時間。
有關在構建中利用構建緩存的大量信息,請參閱有關使用構建緩存的完整指南。它涵蓋了可以改善緩存的不同方案,並詳細討論了爲構建啓用緩存時需要注意的不同警告。
圖13.檢查構建的構建緩存的性能
這將顯示有關與緩存交互的任務數量,使用了哪個緩存以及這些緩存條目的傳輸率和打包/解壓縮率的統計信息。
還有一個“任務執行(Task execution)” 選項卡,其中顯示了詳細信息,包括已執行任務的可緩存性。單擊任何類別將帶您進入“時間軸(Timeline)”屏幕,突出顯示該類別的任務。
圖14.面向任務的性能視圖
圖15.僅帶有“不可緩存”任務的時間線屏幕
隨後,在“時間軸(Timeline)”屏幕上按任務持續時間排序將突出顯示具有極大節省時間潛力的任務。上面的構建掃描顯示了,:task1
和:task3
可以對其進行改進並使其可緩存,並明確說明了爲什麼將其視爲不可緩存的原因。
1.7.守護進程
1.7.1.在舊的Gradle版本上啓用守護程序
Gradle守護程序是一種用於提高Gradle性能的機制。從Gradle 3.0開始,守護程序默認情況下處於啓用狀態,但是,如果您使用的是舊版本,則一定要在本地開發人員計算機上啓用它。這樣,您會發現構建速度有了很大的提高。您可以在2.14用戶指南中瞭解如何進行操作。
在CI計算機上,可以從守護程序獲得的收益取決於您的設置。如果您擁有長期的CI代理,並且構建了許多都使用相同Gradle版本和JVM參數的小型項目,則該守護程序可以減少週轉時間。如果您的項目規模更大或更多樣化,那麼您可能不會看到太多好處。通常,保留守護程序是安全的,因爲Gradle 3.0引入了運行狀況監視,它將關閉守護程序以減少內存壓力。
1.7.2.調整守護程序的堆大小
默認情況下,Gradle將爲您的構建保留1GB的堆空間,對於大多數項目而言,這是足夠的,尤其是如果您遵循本指南中關於分叉編譯的建議。但是,某些非常大的構建可能需要更多內存來保存Gradle的模型和緩存。如果是這種情況,則可以在gradle.properties文件中檢入更大的內存要求:
gradle.properties
org.gradle.jvmargs=-Xmx2048M
1.8.Java項目建議
以下建議特定於使用java插件或其他JVM語言之一的項目。
1.8.1.運行測試
在許多項目中,很大一部分構建時間都由運行的測試任務組成。這些可能是單元測試和集成測試的混合,而後者往往要慢得多。構建掃描可以幫助您確定最慢的測試,這應該是性能改進的主要重點。
圖16. Tests屏幕,其中包含按項目進行的測試,並按持續時間排序
如上所示,構建掃描爲運行測試的所有項目提供了交互式測試報告。
Gradle有幾種方法可以幫助您更快地完成測試:
-
並行測試執行
-
流程分叉選項
-
禁用報告生成
讓我們依次看一下每個。
1.8.1.1.並行測試執行
Gradle會愉快地並行運行多個測試用例,當您有多個CPU內核並且不想浪費它們中的大多數時,這很有用。要啓用此功能,只需在相關Test
任務上使用以下配置設置:
Groovy
build.gradle
tasks.withType(Test) {
maxParallelForks = 4
}
Kotlin
build.gradle.kts
tasks.withType<Test> {
maxParallelForks = 4
}
通常的方法是使用小於或等於您擁有的CPU內核數的數字,例如以下算法:
Groovy
build.gradle
tasks.withType(Test) {
maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
}
Kotlin
build.gradle.kts
tasks.withType<Test> {
maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).takeIf { it > 0 } ?: 1
}
請注意,如果確實並行運行測試,則必須確保它們是獨立的,即不共享資源,無論是文件,數據庫還是其他資源。否則,測試可能會以隨機且不可預測的方式相互干擾。
1.8.1.2.分叉選項
默認情況下,Gradle將在單個分叉的VM中運行所有測試。如果有很多測試或一些非常消耗內存的測試,這可能會成問題。一種選擇是使用大堆運行測試,但是您仍然會受到系統內存的限制,並且可能會遇到大量垃圾回收,從而降低了測試速度。
另一個選擇是在運行一定數量的測試後派生新的測試VM。您可以使用以下forkEvery
設置執行此操作:
Groovy
build.gradle
tasks.withType(Test) {
forkEvery = 100
}
Kotlin
build.gradle.kts
tasks.withType<Test> {
setForkEvery(100)
}
請注意,分叉VM是一項相對昂貴的操作,因此此處的小值將嚴重影響測試的性能。
1.8.1.3.報告生成
Gradle默認會自動創建測試報告,無論您是否要查看它們。生成報告需要時間,從而降低了整體構建速度。報表絕對有用,但是每次運行構建時都需要報表嗎?也許您只關心測試是否成功。此外,如果您使用的是構建掃描,則無需在本地生成報告。
要禁用測試報告,只需添加以下配置:
Groovy
build.gradle
tasks.withType(Test) {
reports.html.enabled = false
reports.junitXml.enabled = false
}
Kotlin
build.gradle.kts
tasks.withType<Test> {
reports.html.isEnabled = false
reports.junitXml.isEnabled = false
}
此示例適用於Test
Java插件添加的默認任務,但您也可以將配置應用於Test
您擁有的任何其他任務。
要記住的一件事是,您可能需要有條件地禁用或啓用報告,否則您將不得不編輯構建文件以查看它們。例如,您可以基於項目屬性啓用報告:
Groovy
build.gradle
tasks.withType(Test) {
if (!project.hasProperty("createReports")) {
reports...
}
}
Kotlin
build.gradle.kts
tasks.withType<Test> {
if (!project.hasProperty("createReports")) {
reports...
}
}
1.8.2.編譯Java
Java編譯器非常快,特別是與JVM上的其他語言相比。但是,如果您要編譯數百個非平凡的Java類,那麼即使是很短的編譯時間也會加起來很重要。當然,您可以升級硬件以加快編譯速度,但這可能是一個昂貴的解決方案。Gradle提供了一些基於軟件的解決方案,可能更適合您:
-
編譯器守護程序
-
避免編譯和Java庫插件
-
增量編譯
1.8.2.1.編譯器守護程序
Gradle Java插件允許您通過對任何JavaCompile
任務使用以下配置,將編譯器作爲單獨的進程運行:
Groovy
build.gradle
<task>.options.fork = true
Kotlin
build.gradle.kts
<task>.options.isFork = true
或者,更常見的是將配置應用於所有 Java編譯任務:
Groovy
build.gradle
tasks.withType(JavaCompile) {
options.fork = true
}
Kotlin
build.gradle.kts
tasks.withType<JavaCompile> {
options.isFork = true
}
此過程在構建期間將被重用,因此分叉開銷很小。分叉的好處是,內存密集型編譯是在不同的進程中進行的,從而導致主Gradle守護程序中的垃圾回收量大大減少。守護程序中較少的垃圾收集意味着Gradle的基礎架構可以運行得更快,尤其是在您還使用--parallel
的情況下。
它對於小型項目不太可能有用,但是如果單個任務將近一千個或更多的源文件一起編譯,則絕對應該考慮使用它。
1.8.2.2.避免編譯
很多時候,您只是在更改代碼的內部實現細節,例如,編輯方法主體。從Gradle 3.4開始,這些所謂的與ABI兼容的更改不再觸發下游項目的重新編譯。在具有深層依賴鏈的大型多項目構建中,這尤其可以縮短構建時間。
注意:如果使用註釋處理器,則需要顯式聲明它們,以便避免編譯。在用戶指南中閱讀有關避免編譯的更多信息。
1.8.2.3.Java庫插件
長期以來,您將使用該compile
配置聲明您的編譯時依賴關係,而所有這些依賴關係都將泄漏到下游項目中。從Gradle 3.4開始,您現在可以清楚地區分哪些依賴項是您的一部分,api
而哪些只是implementation
細節。實現依賴項不會泄漏到下游項目的編譯類路徑中,這意味着當此類實現細節發生更改時,它們將不再被重新編譯。
Groovy
build.gradle
dependencies {
api project('myUtils')
implementation 'com.google.guava:guava:21.0'
}
Kotlin
build.gradle.kts
dependencies {
api(project("myUtils"))
implementation("com.google.guava:guava:21.0")
}
這可以大大減少大型多項目構建中的單個更改的“漣漪”效應。該implementation
配置在java
插件中可用。api
依賴項只能由應該使用新java-library
插件的庫定義。
1.8.2.4.增量編譯
Gradle可以分析直至單個類級別的依賴關係,以便僅重新編譯受更改影響的類。自Gradle 4.10起,增量編譯是默認設置。在舊版本中,您可以這樣激活它:
Groovy
build.gradle
tasks.withType(JavaCompile) {
options.incremental = true
}
Kotlin
build.gradle.kts
tasks.withType<JavaCompile> {
options.isIncremental = true
}
1.8.低級分析
有時,即使您的構建腳本可以正確完成所有工作,您的構建也會變慢。這通常歸結爲插件和自定義任務效率低下或資源受限。查找此類瓶頸的最佳方法是使用Gradle Profiler。Gradle Profiler允許您定義諸如“在進行ABI重大更改後運行'彙編'”之類的方案,然後自動運行構建幾次以對其進行預熱並收集性能分析數據。它可用於生成構建掃描,或與其他主要的探查器(如JProfiler和YourKit)一起使用。使用這些方法級別的探查器通常可以幫助您在自定義插件中找到效率低下的算法。如果您發現Gradle本身的某些功能在減慢您的構建速度,請不要猶豫在以下位置發送探查器快照:[email protected]。
1.8.1.個人資料報告
如果您無法訪問互聯網或出於其他原因不使用構建掃描,則可以使用--profile
命令行選項:
$ gradle --profile <tasks>
這將導致生成HTML報告,您可以在根項目的build/reports/profile目錄中找到該報告。每個配置文件報告的名稱均帶有時間戳,以避免覆蓋現有的時間戳。
該報告顯示了運行構建所花費的時間的詳細信息,儘管沒有構建掃描詳細得多。這是真實配置文件報告的屏幕截圖,顯示了Gradle使用的不同類別:
圖17.一個示例報告
1.8.2.瞭解效果類別
構建掃描和本地配置文件報告都將構建執行分爲相同的類別,下面將對其進行詳細說明。
1.8.2.1.啓動
這反映了Gradle的初始化時間,其中主要包括
-
JVM初始化和類加載
-
如果使用包裝器,則下載Gradle發行版
-
如果尚未運行合適的守護程序,則啓動守護程序
-
執行任何Gradle初始化腳本所花費的時間
即使構建執行的啓動時間很長,通常在後續運行中啓動時間也會急劇下降。構建的啓動時間持續緩慢的主要原因是您的初始化腳本中存在問題。仔細檢查您正在做的工作是否必要且性能儘可能好。
1.8.2.2.設置和buildSrc
Gradle啓動並運行後不久,它將初始化您的項目。這通常僅意味着處理設置文件,但是如果您在buildSrc目錄中具有自定義構建邏輯,則也將對其進行構建。
樣本概要文件報告顯示了此類別的時間超過1.6秒,其中絕大部分時間用於構建buildSrc項目。幸運的是,一旦buildSrc構建一次,該部分就不會花很長時間,因爲Gradle會考慮到它是最新的。最新的檢查仍然需要花費一些時間,但相差無幾。如果確實在持續耗時的buildSrc階段遇到問題,則應考慮將其分解到一個單獨的項目中,該項目的JAR工件會添加到構建的類路徑中。
設置文件中很少包含計算或IO昂貴的代碼。如果您發現Gradle需要大量時間來處理它,則應使用更傳統的配置方法,例如Gradle Profiler來確定原因。
1.8.2.3.載入項目
通常,加載項目不會花費大量時間,您也無法對其進行任何控制。花費的時間基本上取決於構建中項目的數量。
1.9.Android版本建議
到目前爲止,所有討論都適用於Android版本,因爲它們基於Gradle。 Android也引入了自己的性能因素。 Android Studio團隊已編寫了自己的出色性能指南。 您還可以觀看Google IO 2017附帶的演講。
1.10.摘要
性能不是構建過程中的事後思考-它是影響團隊生產力和幸福感的關鍵功能。Gradle團隊專注於儘可能快地實現Gradle構建,因爲他們知道您的時間很寶貴。即使這樣,Gradle也支持各種各樣的構建,這意味着有時默認設置並不總是適合您的項目。爲了幫助您優化構建,本指南向您介紹了設置和選項,使您可以自定義Gradle的行爲以最適合您的特定構建。
除了這些設置之外,請記住,構建時間的兩個主要因素是配置和任務執行,儘管前者的基本成本幾乎隨Gradle的每個主要發行版而下降。就配置階段而言,現在您應該對需要避免的陷阱有了一個很好的瞭解。使用任務執行,您可以控制更多,因爲您可以避免運行任務或過於頻繁地運行它們。您還可以編寫自己的任務以使其效率更高。
您還可以利用構建掃描在構建和配置過程中深入瞭解性能熱點。此外,構建掃描使您可以輕鬆共享構建的特定方面,並與同事進行協作。
希望本指南中的想法可以幫助您減少構建時間並改善整體構建體驗!
1.11.幫助完善本指南
有意見或問題嗎?找到錯字了?像所有Gradle指南一樣,幫助只是GitHub問題而已。請在gradle-guides/performance中添加一個問題或請求,我們將盡快與您聯繫。