简体   繁体   English

AspectJ 计算执行时间

[英]AspectJ to calculate execution time

In this scenario, method1 calls method2 .在这种情况下, method1调用method2

There is a pointcut and advice defined on each of the method execution to calculate time taken by the method.在每个方法执行上定义了一个切入点和建议,以计算该方法所花费的时间。 So,所以,

   advice of `method1` prints 10 seconds; 

   advice of `method2` prints 6 seconds;

I want to print the time taken by method1 excluding the time taken by method2 .我想打印method1花费的时间,不包括method2花费的时间。 That is I want 10-6 = 4 seconds as result那就是我想要10-6 = 4 seconds的结果

How can I acheive this using aspect ?我怎样才能使用方面来实现这一点?

Expected : 4 seconds预期:4 秒

Code snippet :代码片段:

public int method1( String input ){
   User user = null;
   // something .. 
   method2(input, input2);
   return 100;
}

Here is the check method in aspect component:这是方面组件中的检查方法:

@PointCut("....something correct")
public void endpoint (ProceedingJoinPoint pjp) throws Throwable {

@Around("endpoint")
public void timeTaken(ProceedingJoinPoint pjp) throws Throwable {
     
     //code to calcualte start time
     pjp.proceed();
     printf("the total time " : currentTime - startTime);
}

It might be possible, but complex to emulate a profiler by means of AOP.通过 AOP 模拟分析器可能是可能的,但很复杂。 The reason is that you would need to calculate a method call tree and record it along with the execution times in a separate data structure.原因是您需要计算一个方法调用树并将其与执行时间一起记录在一个单独的数据结构中。 When logging, you would have to subtract execution times of everything in a subtree from the measured time.记录时,您必须从测量的时间中减去子树中所有内容的执行时间。 This would cost CPU cycles, memory and lots of maintenance work.这将花费 CPU 周期、内存和大量维护工作。 One more issue is asynchronous code, ie multi-threading.另一个问题是异步代码,即多线程。

My recommendation is to take a step back and think about why you even think you need this.我的建议是退后一步,想想为什么你甚至认为你需要这个。 The only reason is that your method1 mixes (time-consuming) logic with calling other methods also containing (time-consuming) logic.唯一的原因是你的method1混合物(耗时)与调用其他方法还包括(耗时)逻辑的逻辑。

Have you ever heard about the Integration Operation Segregation Principle (IOSP) ?您听说过集成操作隔离原则 (IOSP)吗? It basically states that there are two types of methods:它基本上说明有两种类型的方法:

  • operations containing logic包含逻辑的操作
  • integrations only calling operations集成仅调用操作

If you apply this principle to your own code, you indirectly get the numbers you are interested in as a side effect.如果你将这个原则应用到你自己的代码中,你会间接得到你感兴趣的数字作为副作用。

So if you run this aspect所以如果你运行这个方面

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class TimerAspect {
  @Around("execution(* *(..)) && within(de.scrum_master..*) && !within(TimerAspect)")
  public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long startTime = System.nanoTime();
    try {
      return joinPoint.proceed();
    }
    finally {
      System.out.println(joinPoint + " -> " + (System.nanoTime() - startTime) / 1000000 + " ms");
    }
  }
}

against this application反对这个申请

package de.scrum_master.app;

public class Application {
  public static void main(String[] args) throws InterruptedException {
    new Application().method1("foo");
  }

  public int method1(String input) throws InterruptedException {
    Thread.sleep(300);
    method2(input, 11);
    Thread.sleep(100);
    return 100;
  }

  public void method2(String input, int i) throws InterruptedException {
    Thread.sleep(600);
  }
}

you get the problematic output you don't like:你会得到你不喜欢的有问题的输出:

execution(void de.scrum_master.app.Application.method2(String, int)) -> 604 ms
execution(int de.scrum_master.app.Application.method1(String)) -> 1009 ms
execution(void de.scrum_master.app.Application.main(String[])) -> 1010 ms

But if you refactor your application code according to the IOSP like this但是如果你像这样根据 IOSP 重构你的应用程序代码

package de.scrum_master.app;

public class Application {
  public static void main(String[] args) throws InterruptedException {
    new Application().integration("foo");
  }

  public int integration(String input) throws InterruptedException {
    operation1();
    operation2(input, 11);
    operation3();
    return 100;
  }

  private void operation1() throws InterruptedException {
    Thread.sleep(300);
  }

  public void operation2(String input, int i) throws InterruptedException {
    Thread.sleep(600);
  }

  private void operation3() throws InterruptedException {
    Thread.sleep(100);
  }
}

the application still does the same, but all logical and possible expensive operations are factored out into their own methods.应用程序仍然做同样的事情,但所有逻辑和可能的昂贵操作都被分解到它们自己的方法中。 operation2 is the same as method2 before, operation1 is the logic happening before and operation3 is the logic happening after and operation2 . operation2与之前的method2相同, operation1是之前发生的逻辑, operation3是之后发生的逻辑operation2 Now not only is your integration (formerly method1 ) cleaner and easier to read but also do you get separate timing logs for each operation:现在,不仅是你的integration (前身method1 )更清洁和更容易阅读,但也做你为每个操作单独计时日志:

execution(void de.scrum_master.app.Application.operation1()) -> 302 ms
execution(void de.scrum_master.app.Application.operation2(String, int)) -> 599 ms
execution(void de.scrum_master.app.Application.operation3()) -> 100 ms
execution(int de.scrum_master.app.Application.integration(String)) -> 1005 ms
execution(void de.scrum_master.app.Application.main(String[])) -> 1005 ms

Of course the Thread.sleep operations are just symbolical representations for whatever application logic you might have: if or switch - case statements, loops, external resource (file, database) handling, complex calculations or whatever.当然, Thread.sleep操作只是您可能拥有的任何应用程序逻辑的符号表示: ifswitch - case语句、循环、外部资源(文件、数据库)处理、复杂计算等等。

Bottom line: Go refactor!底线:去重构! Don't mix operations and integrations if you want to time, test, refactor, use them separately.如果您想分别计时、测试、重构、使用它们,请不要混合操作和集成。 Alternatively, use a profiler.或者,使用分析器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM