2013-11-25 2 views
2

Я пытаюсь использовать синхронизированный блок java с использованием ASM. Проблема в том, что после инструментария время выполнения синхронизированного блока занимает больше времени. Здесь он увеличивается с 2 мсек до 200 мсек в ящике Linux.Синхронизированный блок занимает больше времени после работы с 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 время от obove заявления СОП):

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' находятся в синхронизированном блоке. Кстати, что такое '' '? Какие знания о конфликте происходят при синхронизации на этом объекте? – Holger

+0

** s ** - объект String, по которому выполняется синхронизация. здесь я использую только один поток, а блок синхронизации работает 10 раз. Каждое время (t2-t1) вычисляется. Я использую currentTimeMillis внутри блока bcoz, это время обслуживания этого блока, которое одинаково для всех потоков. Это мой мотив, почему он так сильно растет после инструментария. Пожалуйста, не задумывайтесь об этом сейчас. По крайней мере, время выполнения должно оставаться неизменным. – user3032258

+1

Вы измеряете способность JVM оптимизировать неиспользуемый/бессмысленный код, и кажется, что добавление оператора печати (эффект видимого снаружи) в определенном месте нарушает оптимизацию. Кстати, 'String' является необычным объектом для синхронизации. Не рекомендуется. – Holger

ответ

1

Причина заключается в внутренних компонентах JVM, которые вы использовали для запуска кода. Я предполагаю, что это была JVM HotSpot, но приведенные ниже ответы одинаково правы для большинства других реализаций.

Если вы вызываете следующий код:

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

Это будет переведен непосредственно в байт-код Java компилятором Java, но во время выполнения виртуальная машина будет легко увидеть, что этот код ничего не делает. Выполнение этого кода не будет влиять на внешний мир (приложение), так почему 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, что означает, что вы не можете быть абсолютно уверены в их личности. Это означает, что синхронизация может работать или не работать, и вы можете даже синхронизировать другие части вашего кода.

Смежные вопросы