有没有办法按类跟踪java编译时间?

时间:2018-01-04 19:39:51

标签: java performance compilation

我参与了一个包含多个java gradle模块的项目。其中两个最近合并,显着提高了编译时间。我读过使用推断的泛型和重载方法会增加编译时间。

是否有任何工具可以帮助在Java中找到慢编译时类?还是一个指出潜在速度陷阱的短绒?

1 个答案:

答案 0 :(得分:0)

我一直在寻找一种方法来做同样的事情,不得不想出我自己的方法,我将在下面描述。

首先,请注意,当您使用 javac 编译类时,您可以传递 -verbose 标志,以便编译器记录正在发生的一切(它将输出发送到 stderr)。

在 Gradle 中,您可以将该选项传递给编译器,如下所示:

compileJava {
    options.verbose = true
}

我的 hello world 课程看起来像这样(隐藏了很多不相关的东西):

[parsing started SimpleFileObject[/Users/renato/programming/experiments/java/Main.java]]
[parsing completed 21ms]
[loading /modules/jdk.jsobject/module-info.class]
[loading /modules/jdk.management/module-info.class]

...

[checking Main]
[loading /modules/java.base/java/io/Serializable.class]
[loading /modules/java.base/java/lang/AutoCloseable.class]
[loading /modules/java.base/java/lang/Class.class]
[loading /modules/java.base/java/lang/System.class]
[loading /modules/java.base/java/io/PrintStream.class]
[loading /modules/java.base/java/lang/Appendable.class]
[loading /modules/java.base/java/io/Closeable.class]
[loading /modules/java.base/java/io/FilterOutputStream.class]
[loading /modules/java.base/java/io/OutputStream.class]
[loading /modules/java.base/java/io/Flushable.class]
[wrote Main.class]
[total 229ms]

如您所见,您可以捕获 stderr,然后计算它显示 parsing started ...Main.javawrote Main.class 之间的时间。

如果您跟踪每个源文件和等效类文件的消息,您应该能够获得有关编译每个类所用时间的准确信息。

不幸的是,我认为 Gradle 不允许我们使用 Java 编译器的输出,因此我们需要使用其他东西来为我们运行 Gradle 并解析该输出。

这是一个使用 Groovy 脚本执行此操作的示例(即使在 Windows 中,只需稍作修改即可,或者仅使用 javacmvn 进行编译):

def projectDir = new File(System.getProperty('user.home') + '/programming/experiments/gradle')
def javaSourcesRoot = new File(projectDir, 'src/main/java').absolutePath
def classFilesRoot = new File(projectDir, 'build/classes/java/main').absolutePath
def sourcesPrefix = "[parsing started SimpleFileObject[$javaSourcesRoot/"
def donePrefix = "[wrote $classFilesRoot/"

log "Starting Gradle"
def gradle = ['gradle', 'compileJava'].execute((List) null, projectDir)

def stdout = new StringBuilder()
def stderr = new StringBuilder()


gradle.consumeProcessOutput(stdout, stderr)

assert gradle.waitFor() == 0: "$stdout\n$stderr"

def classInfo = [:]

stderr.eachLine { line ->
    if (line.startsWith(sourcesPrefix)) {
        def className = (line - sourcesPrefix - '.java]]').replaceAll(File.separator, '.')
        log "Started compiling class $className"
        classInfo[className] = [startTime: System.currentTimeMillis()]
    }
    if (line.startsWith(donePrefix)) {
        def className = (line - donePrefix - '.class]').replaceAll('/', '.')
        log "Done compiling class $className"
        classInfo[className].endTime = System.currentTimeMillis()
    }
}

def results = classInfo.collect { String className, Map info ->
    "${className.padRight(20)} ${info.endTime - info.startTime}"
}.join('\n')

log "Results:\n$results"

void log(msg) {
    println "${new Date()} - $msg"
}

我的小型示例项目的结果:

Fri Jul 23 16:39:18 CEST 2021 - Starting Gradle
Fri Jul 23 16:39:20 CEST 2021 - Started compiling class other.JavaType
Fri Jul 23 16:39:20 CEST 2021 - Started compiling class my.Main
Fri Jul 23 16:39:20 CEST 2021 - Done compiling class other.JavaType
Fri Jul 23 16:39:20 CEST 2021 - Done compiling class my.Main
Fri Jul 23 16:39:20 CEST 2021 - Results:
other.JavaType       9
my.Main              4

时间以毫秒为单位,所以 my.Main 是一个 hello world 类,编译需要 9 毫秒……other.JavaType 是一个空类,需要 4 毫秒。