2013-11-25 29 views
2

我正在嘗試使用ASM儀器Java同步塊。問題是,在測試之後,同步塊的執行時間需要更多時間。在Linux上,它從2毫秒增加到200毫秒。同步塊需要更多的時間後,使用ASM

我正在通過識別MonitorEnter和MonitorExit操作碼來實現這一點。

我試着在三級的儀器1.在MonitorEnter之前2.在MonitorEnter之後3.在MonitorExit之前。 1和3一起工作正常,但是當我做2時,執行時間急劇增加。

即使我們測試另一個單一的SOP語句,它只打算執行一次,它會給出更高的值。 下面示例代碼(質數,10圈):

for(int w=0;w<10;w++){ 
synchronized(s){ 
    long t1 = System.currentTimeMillis(); 
    long num = 2000; 
for (long i = 1; i < num; i++) { 
     long p = i; 
    int j; 
    for (j = 2; j < p; j++) { 
      long n = p % i; 
     } 
    } 
long t2 = System.currentTimeMillis(); 
System.out.println("Time>>>>>>>>>>>> " + (t2-t1)); 
} 

這裏instrumention代碼(這裏System.currentMilliSeconds()給出了在該instrumention發生的時間,其沒有執行時間的措施,excecution時間是從SOP聲明):

public void visitInsn(int opcode) 
    { 
     switch(opcode) 
     { 
      // Scenario 1 
     case 194: 
      visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io /PrintStream;"); 
      visitLdcInsn("TIME Arrive: "+System.currentTimeMillis()); 
      visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); 
      break; 

     // scenario 3 
     case 195: 
      visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 
      visitLdcInsn("TIME exit : "+System.currentTimeMillis()); 
      visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); 
      break; 
     } 

     super.visitInsn(opcode); 

     // scenario 2 
     if(opcode==194) 
     { 
      visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 
      visitLdcInsn("TIME enter: "+System.currentTimeMillis()); 
      visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); 

     } 

     } 

我無法找到它發生的原因,以及如何糾正它。

在此先感謝。

+0

您不測量同步本身,因爲'currentTimeMillis'調用都在synchronized塊中。順便說一下,'s'是什麼?你有什麼關於在同步該對象時發生的爭用的知識? – Holger

+0

** s **是同步完成的String對象。這裏我只使用一個線程,同步塊運行10次。每次(t2-t1)時間都被計算出來。我正在使用塊內的currentTimeMillis bcoz,它是這個塊的服務時間,對於所有線程都是一樣的。這是我的動機,爲什麼它在儀器儀表後增加很多。請不要考慮爭用部分。至少執行時間應該保持不變。 – user3032258

+1

您正在測量JVM優化未使用/無意義代碼的能力,並且似乎在特定位置添加打印語句(該效果在外部可見)會干擾優化。順便說一下,'String'是一個不同尋常的對象。不是真的推薦。 – Holger

回答

1

原因在於您用於運行代碼的JVM的內部。我假設這是一個HotSpot JVM,但下面的答案對於大多數其他實現同樣適用。

如果觸發下面的代碼:

int result = 0; 
for(int i = 0; i < 1000; i++) { 
    result += i; 
} 

這將直接由Java編譯器,但在運行時轉換成Java字節碼的JVM會很容易地看到,該代碼沒有做任何事情。執行此代碼對外部(應用程序)世界沒有影響,那麼爲什麼JVM應該執行它?這個考慮正是編譯器優化爲你做的。

不過,若你觸發了下面的代碼:

int result = 0; 
for(int i = 0; i < 1000; i++) { 
    System.out.println(result); 
} 

Java運行時無法優化掉你的代碼了。整個循環必須始終運行,因爲System.out.println(int)方法總是在做些什麼real這樣您的代碼將運行得更慢。

現在讓我們看看你的例子。在你的第一個例子中,你基本上寫這個代碼:

synchronized(s) { 
    // do nothing useful 
} 

這整個代碼塊可以很容易地被Java運行時刪除。這意味着:將不會有同步!在第二個例子中,你是不是寫這個:

synchronized(s) { 
    long t1 = System.currentTimeMillis(); 
    // do nothing useful 
    long t2 = System.currentTimeMillis(); 
    System.out.println("Time>>>>>>>>>>>> " + (t2-t1)); 
} 

這意味着有效的代碼可能會是這樣的:

synchronized(s) { 
    long t1 = System.currentTimeMillis(); 
    long t2 = System.currentTimeMillis(); 
    System.out.println("Time>>>>>>>>>>>> " + (t2-t1)); 
} 

這裏重要的是,這種優化的代碼將是有效同步關於執行時間的重要區別是什麼。基本上,您正在測量同步某些內容所花費的時間(如果JVM意識到s未在您的代碼中的其他位置鎖定,那麼即使是在幾次運行之後,也可能會優化這些時間(流行詞:臨時優化,可能需要去最佳化。如果將來加載代碼也將同步上s

你真的應該閱讀:

您的測試例如錯過了一個預熱,這樣您也在測量JVM將多長時間用於字節碼以進行機器代碼優化。

附註:同步String幾乎總是一個壞主意。你的字符串可能是,也可能不是interned,這意味着你不能完全確定他們的身份。這意味着,同步可能會或可能不會工作,甚至可能會導致代碼的其他部分同步。