Pull to refresh

Comments 21

UFO just landed and posted this here
Насколько же провокационной моя статья оказалась. Молодцы :)
Не провокационная, а на редкость полезная и познавательная :)
а можно узнать по поводу причины сортировки предикатов?
гитхаб

отключение данной сортировки уменьшает время выборки с 33ms до 15ms
В этой программе (на примитивном уровне) показывается работа SQL оптимизатора. Предикаты сортируются по селективности — чем меньше мы записей выберем с участием этого предиката, тем раньше мы должны его применить.
Если в app.java поправить
//27 строка
if(random.nextBoolean()) { finder = finder.withAmount(0, 0); } 
на 
if(random.nextBoolean()) { finder = finder.withAmount(0, 1000000); }
и 
//35 строка
if(random.nextBoolean()) { finder = finder.withCode(rr, rr + 5); }
на
if(random.nextBoolean() || true) { finder = finder.withCode(rr, rr + 5); }

мы ухудшим возможность отсечения ненужных строк по amount. Без сортировки предикатов amount так и останется первым и ухудшит общее время выполнения, а с сортировкой уйдет на вторую позицию.
с одной стороны я понимаю ЗАЧЕМ это все сделано, но цитируя TheShade:

– “Real world strikes back!”
– Исследуем взаимодействие софта с железом на типичных данных
• Производительность уже нельзя предсказать
• Производительность можно только измерить

замерил 20ms с отключенной сортировкой vs 31ms с включенной

p.s. конечно по хорошему нужно допилить jmh, но пока в качестве костыля увеличил количество прогревов

for (int i = 0; i < 100; i++) {
long millis1 = System.currentTimeMillis();
store.find2(finder);
long endMillis1 = System.currentTimeMillis();
System.out.println(«Elapsed time (warm) :» + (endMillis1 — millis1) + «ms»);
}

в итоге с отключенной сортировкой действительно первых пару раз еще выполняется быстрее, но потом выходит на стабильное значение в 30-31ms (с 17ms в первой попытке), в то время как с отключенной сортировкой опускается до 19-20ms (33ms в первой попытке).
Основоной посыл статьи не в абсолютных цифрах :). Сортировка требует времени. В вашем случае это время больше, чем выгода от перестановки предикатов. Почему это так — для ответа на этот вопрос нужно смотреть листинги работы JIT, как это сделать.
не понял, что я именно должен найти в листиге jit?
то что сортировка занимает время сравнимое со всем поиском?

по поводу кода:
0) вносим ваши изменения для матчера
1) смотрим какой порядок выбора предложен после сортировки
2) enum RecordFields { CODE, AGE, AMOUNT, GENDER, HEIGHT } // порядок который мы получим после анализа предикатов
3) отключаем сортировку
4) время поиска с оптимальным профилем 9-10ms

на этом фоне 30ms c включенной сортировкой как-то смотрится печально, так как теряем почти 20ms на ней
даже с неоптимальным профилем выдаем 20ms

если посмотреть на первоначальный вариант «профайла», то видно что там порядок полей с сортировкой и так попадает на оптимальный, т.е. тут мы теряем только 15ms на сортировку, что сравнимо с временем выполнения всего кода.

так что преждевременные оптимизации далеко не всегда идут во благо

p.s. еще есть косяк теста в том, что у нас массив полей неизменяемый и jit начинает уже подстраиваться под него =) поэтому по хорошему нужно еще и его регулярно перегенеривать, конечно если это у нас база данных, а не фиксированный набор полей.

p.p.s. как получить асм я представляю и уж если пошла такая пьянка на разминку мозгов:
тыц
в чем причина, что incrementnFieldCall так проседает относительно incrementnFieldCall2, ведь согласно логики производительность должна быть равна
баг уже зарепорчен, но вот поковырять asm вам должно быть прикольно
Ну да, джит. Так, знаете ли, можно всю логику закешировать и радоваться, а то, что холодный старт занимает час, так это фигня, да?
А перед этим еще и компьютер включить надо — это вообще ужас!
Пример на C# мне ThermIt кинул в PullRequest, пока не проверял.
C#-Searching…
Затрачена на поиск по Code 0,0359041
Затрачена на поиск по AmountOfMoney 3E-07
1 stage 0,0520052 seconds.
C#-search took 0,0520052 seconds.
Found rows: 0
Сразу бросилось в глаза, идея, кстати, тоже должна была подсказать:
sb.append("r.getGender() >= " + p.minValue + ...

оно выполняется только 1 раз в процессе генерации метода,
в последующих вызовах сразу забирает из кеша полученный класс

можно конечно написать sb.append для отдельных составляющих, но тогда снижается читабельность
тогда можно было и стрингбилдер не мучать)
Вроде бы при компиляции он всё равно заменит плюсы на append…
да, заменит, но не так как вы себе представляете

sb.append("r.getAmount() >= " + p.minValue + " && r.getAmount() <= " + p.maxValue + " && ");


после компиляции будет

sb.append(
    new StringBuilder()
         .append("r.getAmount() >= ").append(p.minValue)
         .append(" && r.getAmount() <= ").append(p.maxValue)
         .append(" && ").toString()
);


в первом посте предложение про полное исключение созданий лишних объектов.
Ну я как раз так себе и представляю. Но интересный вопрос: если компилятор заменяет плюсы на стрингбилдер, может он и так умеет? Вы не проверяли? Я если честно сразу со стрингбилдером пишу, не люблю когда компилятор за меня меняет код, так что как точно это работает не знаю.
Осталось еще один зубодробительный вариант сделать: Сгенерировать на лету сишный код, собрать его через libclang на лету, а потом подгрузить и зарезолвить получившиеся символы.
Sign up to leave a comment.

Articles