【问题标题】:Timing how long each file takes to compile with ant计算每个文件用 ant 编译的时间
【发布时间】:2016-04-11 02:04:02
【问题描述】:

偶尔对 Java 源文件稍作修改,例如一些额外的显式转换,以帮助编译器将单个 Java 文件的编译时间从 4 分钟缩短到 3 秒(尤其是在 Java 8 中)。

问题是:在一个大型java项目中,你如何找到哪些特定的.java文件编译缓慢?

有没有办法让 Ant 计算编译每个单独的 .java 文件需要多长时间?

【问题讨论】:

  • 理论上,您可以通过自定义 Ant 目标使用 JSR 199(Java 编译器 API)调用编译器来执行此操作,但这听起来需要做很多工作。你能看看 .class 文件的时间戳吗?
  • 可能。然后只需检查修改日期之间的增量?我想知道 javac 命令是否可以包装在一个批处理文件中,该批处理文件需要执行多长时间。然后让 ant 使用其他东西而不是 javac。
  • 问题是 javac 一次没有被调用一个文件。 javac 不是增量编译器,因此必须同时接收每个文件。所以计时 javac 需要多长时间实际上与计时整个编译需要多长时间没有太大区别。
  • (OT) @clinux: 你有一个例子可以提高编译速度吗?
  • @Jayan:编译器在通过大量可变参数(例如 10 个左右)推断类型时表现不佳,当您对每个可变参数参数进行类型转换时,它又会快速运行。

标签: java ant javac


【解决方案1】:

我认为这是可能的。这是我发现的:

如果您使用的是 Java 8,您可以向编译器注册 Plugin 以在编译期间添加一些额外的功能。文档对插件有这样的说法:

预计一个典型的插件会简单地注册一个TaskListener,以便在编译执行期间被通知事件,其余的工作将由任务监听器完成。

因此,您可以设置一个插件来使用 TaskListener,并在生成类时让任务侦听器记录时间戳。

package xyz;
import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;

public class TimestampPlugin implements Plugin {


    @Override
    public String getName() {
        return "Timestamp_Plugin";
    }

    @Override
    public void init(JavacTask task, String... strings) {
        task.setTaskListener(new FileTimestampListener());
    }
}

Documentation for TaskListener。任务监听器被传递一个TaskEvent,它有一个Kind。在您的情况下,听起来您对生成感兴趣。

package xyz;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;

import java.util.HashMap;

public class FileTimestampListener implements TaskListener {
    HashMap<String, Long> timeStampMap = new HashMap<>();

    @Override
    public void started(TaskEvent taskEvent) {
        if(TaskEvent.Kind.GENERATE.equals(taskEvent.getKind())) {
            String name = taskEvent.getSourceFile().getName();
            timeStampMap.put(name, System.currentTimeMillis());
        }
    }

    @Override
    public void finished(TaskEvent taskEvent) {
        if(TaskEvent.Kind.GENERATE.equals(taskEvent.getKind())) {
            String name = taskEvent.getSourceFile().getName();
            System.out.println("Generated " + name + " over " + (System.currentTimeMillis() - timeStampMap.get(name)) + " milliseconds");
        }
    }
}

这是一个简单的示例,但从这里开始设置日志文件之类的东西来存储收集到的信息应该很简单。正如您在插件的 init 函数中看到的那样,可以从命令行将参数传递给插件。

通过使用 -Xplugin 编译器参数指定插件来配置插件。我不知道为什么,但似乎在this page 上没有任何关于它的文档,但可以通过设置一个名为 com.sun.source.util.Plugin 的文件(接口的 FQ 类名)来使用它实施)在您的 META-INF/services 目录中。所以:

META-INF
|-- services
    |-- com.sun.source.util.Plugin

并在该文件中列出您实现该类的 FQ 类名称。所以文件内容是:

xyz.TimestampPlugin

在您的 Ant 任务中,您只需要指定编译器标志 -Xplugin:Timestamp_Plugin(注意这是插件的 getName() 函数提供的名称)。您还需要在类路径或注解处理器路径(如果已指定)上提供已编译的插件和运行时依赖项。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-01
    • 1970-01-01
    • 2012-08-19
    • 2016-06-05
    • 1970-01-01
    • 2015-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多