Pull to refresh
170
0
Андрей @apangin

Пользователь

Send message
Извините. Возможно, вы и анализировали output компилятора и ассемблерный код. Тогда я неправ.
Я ж судил только по очевидным признакам:
— Warm-up phase нет.
— Изначально выводы были основаны на измерениях результатов клиентского компилятора.
— Предотвращение OSR не выполнено.
— Печать и инициализация классов внутри timing phase.
А ведь эти самые главные!
Чертовски правильный вопрос!
Например, в выложенных здесь javatest.zip и javatest2.zip нарушены практически все пункты правил написания микробенчмарков для JVM.
Мне смайлик следовало поставить во фразе про интерпретатор? :)
Тега <irony> явно не хватает.
Давайте перенесем дальнейшую дискуссию в личку, чтоб более не засорять топик.
C:\Windows\Temp>java Test2 (charAt)
240098112
210

C:\Windows\Temp>java Test3 (getChars)
240098112
247

Меньше — лучше, правильно ведь? charAt выигрывает.

C:\Windows\Temp>java -version
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01, mixed mode)

Вы бы еще в чистом интерпретаторе запустили и в нем сравнивали.
Не будет это лучше. От лишнего копирования-то все равно не избавитесь. А вызов метода — это не так страшно. Тем более, что никакого вызова в данном случае в скомпилированном коде нет — метод инлайнится.
Неправильно подозреваете. Одинаково работают. Можете проверить.
Совсем недавно я приводил пример, когда метод в три раза длиннее по байткоду компилируется один-в-один, как и короткий метод.
Потому что проверял и знаю.
А еще это вполне логично даже из общих соображений: toCharArray() создает новый массив, увеличивая нагрузку на память и копируя символы лишний раз, в то время как charAt() достает символ непосредственно из строки.
А можно еще ускорить процентов этак на 50, если избавиться от str.toCharArray(); и пользоваться str.charAt().
Поскольку тезис N1 был воспринят неоднозначно, разрушители легенд решили проверить миф о том, есть ли все-таки разница между работой synchronizedMethodGet и synchronizedBlockGet :)

Для проверки я запустил программку из Листинга 1 с такими параметрами:
java -Xcomp -XX:CompileOnly=SynchronizationExample.synchronizedMethodGet -XX:CompileOnly=SynchronizationExample.synchronizedBlockGet -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly SynchronizationExample

Анализ показал, что машинный код, сгенерированный C1-компилятором для методов synchronizedMethodGet и synchronizedBlockGet оказался одинаковым с точностью до байта! Таким образом, миф разрушен (или подтвержден, кто как предполагал :) Пруф.
Правда? А покажете в байткоде, где это место?

public static void normal();
  Code:
   0:   getstatic       #2; //Field test:I
   3:   iconst_1
   4:   iadd
   5:   putstatic       #2; //Field test:I
   8:   return

public static synchronized void blocking();
  Code:
   0:   getstatic       #2; //Field test:I
   3:   iconst_1
   4:   iadd
   5:   putstatic       #2; //Field test:I
   8:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #3; //Method normal:()V
   3:   invokestatic    #4; //Method blocking:()V
   6:   return
Зависит от архитектуры процессора и сценария использования. На x86 volatile load такой же быстрый, как и обычный load (за исключением volatile long на 32-битной системе, для которого используются FPU инструкции). На однопроцессорной машине тоже все в порядке. Плохо на многопроцессорной машине с volatile store, который сопровождается инструкцией lock add [esp], 0, что выливается во временную блокировку шины данных и инвалидацию кэша процессора.

Синхронизация с помощью мониторов (synchronized блоки) тоже бывает разная. Она почти ничего не стоит до тех пор, пока монитор используется лишь одним тредом (за счет BiasedLocking). Как только второй тред попробует синхронизоваться на том же мониторе, BiasedLocking перестает работать, и задействуются опять же атомарные инструкции с lock префиксом.
Не будет он более быстрым. Для synchronized метода происходит все то же самое, но в самой виртуальной машине при осуществлении вызова.
Комментарии и дополнения:

1. Несмотря на то, что synchronized block выглядит длиннее, в действительности он работает точно так же, как и synchronized method. Во-первых, строки 11-15 не исполняются, они нужны лишь для обработки IllegalMonitorStateException. Во-вторых, monitorenter и monitorexit и сопутствующие операции выполняются в обоих случаях, просто в случае synchronized метода это происходит неявно на уровне виртуальной машины при вызове метода.

3. Начиная с Java 5 ключевое слово volatile имеет еще одно очень важное значение. Доступ к volatile полю окружен data memory barrier'ом, что гарантирует упорядоченность операций чтения-записи памяти относительно доступа к этому полю. Иначе компилятор или сам процессор вправе переупорядочивать инструкции (out-of-order execution).

4. В качестве примера неатомарного доступа к не-volatile полю можно рассмотреть чтение/запись поля типа long на 32х битной архитектуре. Оно реализуется в виде двух операций чтения/записи двух «половинок» поля. Таким образом, если один thread пишет longField = 0x1111111122222222L; longField = 0x3333333344444444L; то другой thread в какой-то момент может теоретически увидеть значение 0x1111111144444444L. Запись volatile поля происходит чуть хитрее, и такой проблемы быть не может.

5. Атомарные примитивы — хороший способ сделать поле thread-safe без дорогой блокировки, т.к. большинство архитектур аппаратно поддерживают атомарные операции типа compare-and-swap.
Ах, да, важный момент: page-файл ни разу не увеличивался, т.е. 8G виртуальной памяти всегда хватало на любую работу.
Задачка по теме а-ля «check your knowledge» ;)
Есть компьютер с Windows 7 x64, 4G RAM, 128G SSD. Page-файл по умолчанию имеет размер 4G.
Докупается еще 4G RAM в целях увеличения производительности. Теперь 8G RAM и page-файл тоже 8G.
Вопрос, что сделать с page-файлом, и, главное, почему?
— оставить как есть
— отключить
— уменьшить
— другой вариант
А кто, наконец, изобретет мобильную клавиатуру для программистов?
Чтоб for (int i = 0; i < size; i++) { } одним жестом набиралось?
О! Да это дает потрясающие средства генерации машинного кода с использованием всех прелестей объектно-ориентированного подхода. Пожалуй, стоит отдельную статейку на эту тему написать. В рамки одного комментария я точно не уложусь :)
Вкратце: динамическая кодогенерация, использование особенностей аппаратной платформы, независимость от синтаксиса встроенного ассемблера, будь то GCC, MSVC или что еще, тесная интеграция с кодом на C/C++.
Шутки-шутками, но лучший макроассемблер на практике получается из C++.
Уже в нескольких проектах использовал ассемблер, написанный на C++ в виде кодогенерирующих функций типа

  void mov(Register dst32, int imm32)     { emit8(0xb8 + dst32); emit32(imm32); }
  void mov(Register dst32, Address src32) { emit8(0x8b); emit_addr(src32, dst32); }
  void push(Register dst32)               { emit8(0x50 + dst32); }
  void call(Address dst32)                { emit8(0xff); emit_addr(dst32, 2); }
  void call(Label target)                 { emit8(0xe8); emit_label(rel32); }
  void build_frame(int size)              { push(epb); sub(esp, size); }
  и т.д.


В результате код на таком «ассемблере» выглядит примерно так:

  build_frame(32);
  Register osr_buf = eax;
  for (int i = 0; i < num_locks; i++) {
    int slot_offset = monitor_offset - i * 8;
    cmp(Address(osr_buf, slot_offset + 4), 0);
    jcc(eq, done);
    mov(ebx, Address(osr_buf, slot_offset));
    mov(address_of_lock(i), ebx);
  }
  bind(done);
Вместо Runnable можно воспользоваться паттерном Listener или просто сделать HttpClient inner-классом SearchableActivity, тогда можно будет вызывать методы SearchableActivity из HttpClient напрямую.
Просто интересно :) А почему warp и corp?
Коли уж так, то напрашиваются haed и cnotent :)

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Works in
Registered
Activity