如何为每个输入文件测量Xtend的完整翻译耗时
测量IFC转TTL的完整翻译耗时方案
你好,针对你遇到的这个问题——无法在doGenerate中捕获输入文件解析时间,又不能修改依赖的ParallelBuilderParticipant类,我整理了几个可行的方案,都不需要改动第三方依赖代码:
方案1:自定义BuilderParticipant代理原实现
这是最直接且可靠的方案,核心思路是自己实现一个BuilderParticipant,把原ParallelBuilderParticipant包装起来,在调用它的handleChangedContents方法前后计时,这样就能覆盖解析输入文件+生成TTL文件的完整流程。
具体步骤:
- 编写自定义的
TimingBuilderParticipant类,继承BuilderParticipant并持有原实例的引用:
import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.runtime.CoreException; import org.eclipse.xtext.builder.BuilderParticipant; import org.eclipse.xtext.builder.IBuildContext; public class TimingBuilderParticipant extends BuilderParticipant { private final BuilderParticipant delegate; public TimingBuilderParticipant(BuilderParticipant delegate) { this.delegate = delegate; } @Override public void handleChangedContents(IBuildContext context, IResourceDelta delta) throws CoreException { // 用nanoTime比currentTimeMillis更适合测量时间间隔,不受系统时间调整影响 long startTime = System.nanoTime(); try { // 调用原ParallelBuilderParticipant的处理逻辑 delegate.handleChangedContents(context, delta); } finally { long endTime = System.nanoTime(); long durationMs = (endTime - startTime) / 1_000_000; // 从delta中提取当前处理的文件 IResource resource = delta.getResource(); if (resource instanceof org.eclipse.core.resources.IFile && resource.getName().toLowerCase().endsWith(".ifc")) { // 输出耗时,可以打Eclipse日志或者写入自定义文件 String logMsg = String.format("完整处理文件 %s 耗时:%d ms", resource.getFullPath().toString(), durationMs); System.out.println(logMsg); // 如果是Eclipse插件,推荐用插件日志 // YourPluginActivator.getDefault().getLog().log( // new org.eclipse.core.runtime.Status( // org.eclipse.core.runtime.IStatus.INFO, // YourPluginActivator.PLUGIN_ID, // logMsg // ) // ); } } } }
- 替换原
ParallelBuilderParticipant的注册:- 找到你项目中注册
ParallelBuilderParticipant的地方(通常是plugin.xml的org.eclipse.xtext.builder.builderParticipant扩展点)。 - 把原来的实现类替换成你自己的
TimingBuilderParticipant,或者如果需要保留原逻辑,可以在代码中实例化原类并传入你的代理类(比如在插件启动类中初始化)。
- 找到你项目中注册
方案2:利用Eclipse Builder的生命周期钩子
如果你的生成逻辑是绑定在Eclipse的Builder上的,也可以自定义一个Builder,在它的build方法中计时——因为Builder的build方法会触发从资源变更检测、输入解析到生成输出的全流程。
示例代码:
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.xtext.builder.XtextBuilder; public class TimingXtextBuilder extends XtextBuilder { @Override protected void build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException { long startTime = System.nanoTime(); try { super.build(kind, args, monitor); } finally { long endTime = System.nanoTime(); long durationMs = (endTime - startTime) / 1_000_000; IProject project = getProject(); System.out.println(String.format("项目 %s 本次构建总耗时:%d ms", project.getName(), durationMs)); } } }
这个方案适合测量整个项目的构建耗时,如果需要单个文件的耗时,结合IResourceDelta遍历变更的资源即可。
方案3:动态代理(进阶,适合无法替换原实现的场景)
如果因为某些原因不能修改注册的BuilderParticipant,可以用Java动态代理或者字节码增强工具(比如Byte Buddy),对ParallelBuilderParticipant的实例创建代理,在调用handleChangedContents前后插入计时逻辑。不过这个方案复杂度较高,需要处理Eclipse插件的类加载环境,适合有一定字节码编程经验的场景。
注意事项
- 因为
ParallelBuilderParticipant是并行处理资源的,每个文件的处理在独立线程中,所以计时逻辑要保证线程安全(上面的示例中每个线程独立记录startTime,天然线程安全)。 - 尽量用
System.nanoTime()替代System.currentTimeMillis(),前者专门用于测量时间间隔,不受系统时间同步、调整的影响,精度更高。
内容的提问来源于stack exchange,提问作者Henry
相关产品推荐
相关产品推荐

