#java #performance #math
#java #Производительность #математика
Вопрос:
Я создаю метод рендеринга на основе плиток для игры, используя Java.
Существует возможность частичного отображения первой строки (и столбца) плиток на экране, и я хочу знать, сколько пикселей этих плиток находится на экране.
Я придумал это (для проверки максимальных координат X разбитых плиток):
int brokenX = xOffset; //xOffset can be any number
while (brokenX < 0){
brokenX = tileSet.getTileSize(); //corrects if the number is negative
}
while (brokenX >= tileSet.getTileSize()){
brokenX -= tileSet.getTileSize(); //corrects if the number is too big
}
Это работает нормально. Но это кажется пустой тратой ресурсов (учитывая, что это будет сделано дважды для каждого кадра).
Есть ли какой-нибудь эффективный способ (битовые маски и тому подобное) сделать это?
Комментарии:
1. Когда вы заметили, что ваша частота кадров была слишком низкой, и профилировали свое приложение, является ли этот фрагмент кода тем, что вы нашли узкое место?
2. Нет, я не профилировал. Я просто проверил время до и после
render()
метода. Это была только первая его часть.
Ответ №1:
Я думаю, вы ищете
brokenX = xOffset % getTileSize();
Я думаю, что вышесказанное должно работать и для отрицательного значения, но если xOffset может быть больше, то вы могли бы сделать
хорошо… хорошо. Я думаю, что простое добавление чека в последнюю строку должно это исправить.
hiddenTiles = xOffset / getTileSize();
xOffset -= hiddenTiles * getTileSize();
if(xOffset < 0) {
brokenX= -(-xOffset % getTileSize());
}
else {
brokenX = xOffset % getTileSize();
}
Или как предполагает Джейсон:
Только одна строчка…
brokenX= (xOffset getTileSize())% getTileSize());
Я действительно думаю, что это должно сработать и для отрицательных смещений. Так что никаких изменений не требуется.
Комментарии:
1. Это был мой первый выбор, но
xOffset
может быть отрицательным или превышатьtileSize
2. Я попробовал ваше решение. Время рендеринга уменьшается со 118 до 86 мс. Но это работает только для положительных значений.
3. хорошо .. хорошо. Я думаю, что простое добавление проверки в последней строке должно это исправить. Скрытые значения = xOffset / getTileSize(); xOffset -= Скрытые значения * getTileSize(); если (brokenX < 0) { brokenX= xOffset % getTileSize(); }
4. Если вы знаете
xOffset > -tileSize
, что, по-видимому, подразумевает @MarlonDias, вы можете просто обработать оба случаяbrokenX = (xOffset tileSize) % tileSize;
. Классический вариант, когда это условие не выполняется, но первое значение по модулю не обязательно, если .brokenX = ((xOffset % tileSize) tileSize) % tileSize;
xOffset > -tileSize
5. @SahilBajaj Я уже опубликовал окончательный код.
brokenX = (xOffset tileSize) % tileSize;
. Вот и все. Никаких операторов if или чего-либо еще.
Ответ №2:
Я бы предложил использовать библиотечную функцию. Очевидно, что это огромный перебор для этой задачи, но в хорошей библиотеке есть гораздо больше. Мой любимый — гуава, и это mod
метод:
int result = x % m;
return (result >= 0) ? result : result m;
Пример должен прояснить это. Я пытаюсь использовать числа 13, 3, -7, -17
по модулю 10
, которые все должны возвращать один и тот же результат.
13 % 10 = 3
все в порядке3 % 10 = 3
, все в порядке-7 % 10 = -7
отрицательный и непригодный для использования в качестве индекса, но с-7 % 10 10 = 3
ним все в порядке.-17 % 10 10 = 3
тоже хорошо.
Итак, для result < 0
или, что эквивалентно m < 0
, вам нужно добавить m
, чтобы получить положительный результат.
Комментарии:
1. Я не понимаю. Не могли бы вы объяснить, что делают эти выражения, пожалуйста?
2. Теперь я понял! Это просто и сокращает время рендеринга на 60000нс. Спасибо 🙂
Ответ №3:
Ниже приведен распространенный способ преодоления негативной проблемы по модулю без условного:
int tileSize = tileSet.getTileSize();
int brokenX = ((xOffset % tileSize) tileSize) % tileSize;
int brokenY = ((yOffset % tileSize) tileSize) % tileSize;
Это работает с тех пор, как:
- Первый модуль гарантирует, что число меньше размера плитки
- Добавление гарантирует, что число является положительным, так как оно было меньше, чем tileSize, и было добавлено tileSize
- Второе значение по модулю гарантирует, что число снова положительное и меньше размера плитки
Комментарии:
1. Конечно, вызов
%
дважды — не самый быстрый способ. Оптимальное выражение имеет примерно такую же длину.2. @maaartinus Предполагая, что вы говорите о том, что написали в своем ответе, он все еще длиннее и, что более важно, менее оптимален с точки зрения использования процессора. JVM обрабатывает по модулю гораздо эффективнее, чем ветвление, вводимое условными обозначениями.
3. Я в этом сильно сомневаюсь. Довольно часто ветвь предсказуема (так как вы часто защищаетесь от отрицательных значений, но на самом деле не ожидаете их) и поэтому почти бесплатна. В противном случае можно использовать условный ход. Обратите внимание, что ребята из Guava довольно сильно оптимизируют. В любом случае, хороший момент, кто-то должен написать бенчмарк (Caliper или JMH), чтобы разрешить спор.
4. На самом деле это довольно быстро. Однако вы можете оптимизировать его немного дальше. Если вы знаете
xOffset
, что никогда не будет<= -tileSize
(что подразумевает описание OP), вы можете сделатьint brokenX = (xOffset tileSize) % tileSize;
и исключить первое значение по модулю (единственная причина, по которой первое значение по модулю должно существовать в первую очередь, — это поместить операнд в диапазон [-tileSize, tileSize], exclusive). То же самое для Y.5. @JasonC Вы, конечно, правы, но я не делал такого предположения, поскольку в прошлом я слишком часто видел, как это оказывалось ошибкой, чтобы воспользоваться этим шансом. Тем не менее, хороший момент.
Ответ №4:
Если размер плитки равен степени двойки, вы могли бы сделать
brokenX = xOffset amp; (tileSize - 1);
Комментарии:
1. На данный момент моему
tileSize
32, но я планирую повторно использовать метод рендеринга для других размеров. Будет ли этот код работать, если я попробуюtileSize = 48
?2. Нет,
tileSize
должно быть степенью двойки (…, 32, 64, 128, 256, …)
Ответ №5:
if (brokenX < 0)
brokenX = tileSizeX - (Math.abs(brokenX) % tileSizeX);