Конечно же при смене.Хранить максимально посчитанную таблицу параметров чара, обновляя ее при смене эквипа/бафов/обнормалов, или пересчитывать каждый тик/действие?
- При добавлении эффекта к сущности - в соответствующие калькуляторы добавляются функции саб-эффектов этого эффекта.
- При снятии эффекта с сущности - из соответствующих калькуляторов удаляются функции саб-эффектов этого эффекта.
Овербаф получается при абьюзе мозга разработчика, который не посчитал нужным сделать нормально и упорол костыль.я правильно понимаю, что из-за абьюза именно этой логики получался овербаф? и он идет через наслоение новых баффов в обход проверки на уже имеющийся такой, или через ошибку стирания старого ?
И я не совсем одуплил из твоего объявнения. при каждом обращении к параметрам чара при изменении состояния или атаке идет пересчет всех функций в калькуляторе (всех статов), или калькулятор посчитал нынешнее состояние чара, создал массив результатов, и пока нет изменения их - он берет значения из этого массива?
Никакие результаты никуда не сохраняются. Все расчеты только в реальном времени и на каждое действие. Буквально на каждое. Т.к там расчет представляет из себя цепочку простейших арифметических действий из набора функций внутри калькулятора.И я не совсем одуплил из твоего объявнения. при каждом обращении к параметрам чара при изменении состояния или атаке идет пересчет всех функций в калькуляторе (всех статов), или калькулятор посчитал нынешнее состояние чара, создал массив результатов, и пока нет изменения их - он берет значения из этого массива?
Никакие результаты никуда не сохраняются. Все расчеты только в реальном времени и на каждое действие. Буквально на каждое. Т.к там расчет представляет из себя цепочку простейших арифметических действий из набора функций внутри калькулятора.
Все проблемы производительности упираются в синхронизацию. Ты не можешь сохранять значения, не синхронизировав этот процесс для множества потоков. Следовательно прочитать это значение ты тоже можешь только из под синхронизированного участка(что логично). Поэтому любое сохранение результата влечет просто дичайшее удорожание процесса расчета. Не стоит недооценивать современные процессоры. Для них простые операции, которые не долбятся постоянно в барьерные инструкции, практически бесплатны(на фоне общего количества вычислений на фоне)
Да, я про это писал выше. В функциях калькулятора не должно быть какой-то сложной логики, любого вида ожиданий и синхронизаций. Т.е например, если функция, которая считает скорость 20 раз в секунду, полезет в базу данных, чтобы проверить, есть ли у твоего клана пассивка на +1 Dex, то это ничем хорошим не кончится.спасибо, про барьерные инструкции сам погуглю )
но тогда выходит для оптимизации и снижения нагрузки стоит упрощать формулы рассчета, и сокращать их параметры
Да, я про это писал выше. В функциях калькулятора не должно быть какой-то сложной логики, любого вида ожиданий и синхронизаций. Т.е например, если функция, которая считает скорость 20 раз в секунду, полезет в базу данных, чтобы проверить, есть ли у твоего клана пассивка на +1 Dex, то это ничем хорошим не кончится.
Как правило, информация о всех клановых скиллах хранится в объекте клана, в памяти. Поэтому доступ к этим данным почти мгновенный. Я просто привел пример некорректной реализации.а как тогда реализовываются клан-скилы? они прописаны изначально в хранилище калькуляторов как инфа о чаре?
package ru.nts.benchmarks;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
public class CalculatorBenchmark {
private Calculator calculator;
private final Random random = new Random();
private final double initValue = random.nextDouble();
@Param({"100000", "1000000", "10000000"})
public int operations;
@Setup
public void setup() {
Func[] funcs = new Func[10];
for (int i = 0; i < 5; i++) {
funcs[i] = new ADD(random.nextInt(10), random.nextDouble() * 100);
funcs[i + 5] = new MUL(random.nextInt(10), random.nextDouble() * 10);
}
calculator = new Calculator(funcs);
}
@Benchmark
public double operations_avg() {
double result = 0;
for (int i = 0; i < operations; i++) {
result = calculator.calc(initValue);
}
return result;
}
public static void main(String[] args) throws RunnerException {
new Runner(new OptionsBuilder().include(CalculatorBenchmark.class.getSimpleName()).build()).run();
}
public interface Func { double calc(double init); int order(); }
record ADD(int order, double value) implements Func {@Override public double calc(double init) { return init + value; }}
record MUL(int order, double value) implements Func {@Override public double calc(double init) { return init * value; }}
private record Calculator(Func[] funcs) {
private Calculator(Func[] funcs) {
this.funcs = Arrays.stream(funcs).sorted(Comparator.comparingInt(Func::order)).toArray(Func[]::new);
}
public double calc(double init) {
double result = init;
for (Func func : funcs) {
result = func.calc(result);
}
return result;
}
}
}
Benchmark (operations) Mode Cnt Score Error Units
CalculatorBenchmark.operations_avg 100000 avgt 5 1,409 ± 0,007 ms/op
CalculatorBenchmark.operations_avg 1000000 avgt 5 13,899 ± 0,025 ms/op
CalculatorBenchmark.operations_avg 10000000 avgt 5 142,827 ± 1,391 ms/op
Из того что я видел обычно вешается бафф или же просто эффект на персонажа, т.е. добавляются формулы различных типов. Вот например добавление таких формул и является примером правил для пересчета. Я в принципе согласен и сделал ужe cache в своей разработке. Не нужно пересчитывать то что можно сохранить. Но главное в том что пересчитываться должно только в определенных правилах а не всегда. Ну а правил... или же ситуаций, не так уж много.Насчет кэширования значений - не стоит еще забывать еще об одной причине того, почему стоит делать перерасчеты каждый раз когда идет обращение к стате - у стат, в конкретном значении могут быть кондишны, которые влияют на использование этого значения в данный момент. Например значение учитывается в расчете только при нахождении в определенной зоне, при уровне хп не выше определенного и т.д. и т.п.
Вот с такими кондишнами в целом и надо достаточно вменяемо работать, чтобы были достаточно простыми и отрабатывали проверки как можно быстрее и оптимальней, а то бывает в них такого нагородят, что только за голову хватаешься - в какой-то из старых сборок видел даже работу с бд в них...
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?