1. Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.
    Скрыть объявление
Скрыть объявление
В преддверии глобального обновления, мы проводим исследования, которые помогут нам сделать опыт пользования форумом ещё удобнее. Помогите нам, примите участие!

Помогите Вопрос по производительности и скорости кода

Тема в разделе "Разработка плагинов для новичков", создана пользователем normalped, 9 ноя 2024.

  1. Автор темы
    normalped

    normalped Участник Пользователь

    Баллы:
    36
    Приветствую! Я слушаю кучу пакетов через ProtocolLib и обрабатываю их текстовые компоненты через js скрипты с помощью движка Rhino. В среднем у меня такая обработка текста занимает 0.01 секунду, при чем ~90% процентов от этого времени занимает именно выполнение скриптов. Все бы ничего, но вот с пакетом Window Items все становится куда хуже. Там приходится обрабатывать сразу огромное кол-во предметов, их имена и описания. Я использую parallelStream для обработки списка с предметами и их описания. В таком случае время полной обработки пакета занимает в среднем от 0.1 до 0.3 сек, когда весь самый большой инвентарь из пакета максимально забит предметами с описаниями на 3 строки. В игре это время очень хорошенько ощущается и становится совсем неприятно пользоваться такими медленными инвентарями.

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

    Уточню, это все я тестирую на своем простом ноуте.

    Я так же пробовал перейти на GraalJS, он по идеи куда быстрее. Но там свои проблемы: после перезагрузки сервера через /reload confirm он ломается и выводит ошибку, так же, как я понял ему надо GraalVM который просто библиотекой к проекту не идет.

    Как вы считаете, это все вообще адекватная идея? Изменится ли ситуация со скоростями если плагин будет стоять на хорошем сервере? Будут ли еще хуже проблемы от увеличения кол-ва игроков?

    Буду рад услышать ваше мнение!
     
  2. HauserGrim

    HauserGrim Активный участник Пользователь

    Баллы:
    96
    Не использовать /reload - одна из основ, которые должны знать все и знают почти все. Эта команда часто что-то ломает. Если нужно, добавь в свой плагин возможность перезагрузить конфиги командой.
    Не особо шарю за JS, но он же довольно медленный? Не знаю на сколько большие скрипты используются, но может лучше написать код на java и брать некоторые параметры из конфига при необходимости?

    Лично для меня звучит не очень.
    Логично, что на более мощном железе всё будет работать быстрее, но вряд ли во много раз.
    Зависит от кода.
     
  3. Автор темы
    normalped

    normalped Участник Пользователь

    Баллы:
    36
    Знаю, но все равно хотелось бы как то пофиксить это.
    Тут наверное проблема больше в выполнении этого js кода на java. То есть если выполнить одинаковый код на этих двух языках то особой разницы не будет. Как я заметил, чисто создание контекста занимает столько же, сколько и выполнение моего js кода, это ужасно...
    Скрипты в основном не большие. У меня упор на неограниченный функционал, я хочу чтобы можно было делать все, что угодно и не зависеть только от того, что "вшито" в плагин.

    Может можно как то js код компилировать типо в байт код java один раз и использовать уже его?
     
    Последнее редактирование: 9 ноя 2024
  4. Dymeth

    Dymeth Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Dymeth
    А чем вообще обусловлена необходимость делать это на JS? Если писать на java, то и никакие контексты создавать не придется. Или я чего-то не понимаю?
     
  5. Автор темы
    normalped

    normalped Участник Пользователь

    Баллы:
    36
    Я же говорил, мне очень хочется сделать неограниченный функционал, js скрипты для этого как раз и нужны. Контекст - это штука из Rhino которая нужна для выполнения скриптов, без неё ничего работать не будет. Вообще, мне вроде как удалось хорошенько ускорить это все. Js код в котором 10,000 раз с помощью цикла над одной переменной проводятся простые математические вычисления занимает 0.001 сек, когда раньше занимал 0.03 сек. Подробнее распишу уже завтра.

    Несмотря на такой буст, проблема с тем пакетом все равно осталась. Его обработка занимает почему то 0.01 сек, иногда и 0.1+, если «пофлудить» этим пакетом. По большей части обработка каждого предмета в пакете занимает к примеру 0.001 сек, но всегда находится один предмет который сожрет ~0.01 сек и увеличит время обработки всего пакета с 0.001 до 0.0 сек например. Не могу понять как так получается, абсолютно все предметы в списке пакета одинаковые.
     
  6. Dymeth

    Dymeth Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Dymeth
    А что мешает делать "неограниченный функционал" на java?
    Если на java такой код выполняется быстрее (а, скорее всего, так и есть), то можно и java-код подгружать динамически и интегрировать в рантайм ASM'ом.

    Другой вопрос в том, что такая гибкость - это максимально небезопасно и для Java, и для JS.
    Куча историй о вирусах в плейсхолдерах PAPI, например. Поэтому советую 10 раз подумать, прежде чем интегрировать подобное.

    Это касается не только JS, а вообще любых операций. Процессоры работают с непостоянной скоростью для приложения. На устройстве запустилась фоновая задача, загрузила ядро - в этот момент на майнкрафт осталось меньше ресурсов. Тут роль может играть даже банально температура проца.
    Поэтому на единичные показатели ориентироваться нельзя, нужно всегда проводить хотя бы миллион выполнений для формирования хоть сколько-то объективной статистики
     
  7. Автор темы
    normalped

    normalped Участник Пользователь

    Баллы:
    36
    Все довольно просто: скрипты можно выполнять один раз, в них имплементировать метод специального интерфейса который будет использоваться в будущем. Мне этот способ подходит, так как у меня скрипты все время одинаковые. Это хоть и уменьшает время, но по сравнению со скоростью выполнения обычного java кода даже близко не стоит. Время выполнения метода из интерфейса который был имплементирован через:
    js скрипт: 0.001959349 сек
    java код: 0.000045638 сек
    Это средние цифры из 1000 проверок.
     
  8. Автор темы
    normalped

    normalped Участник Пользователь

    Баллы:
    36
    Понимаю, но как именно? Например игрок может ввести в тексте код а мой обработчик его выполнит? Я обрабатываю не прям весь текст, только некоторые "части" которые игрок по идеи не сможет ввести. Я могу четко контролировать наличие таких частей в тексте, по этому я слушаю стандартные ивенты (чат, команда, "подготовка наковальни") и отменяю их если они содержат то, что мне не надо. (последний ивент не очень надежный, в нем все равно получится обойти блокировку. это решать буду чуть позже) Вроде как этого должно быть достаточно. На таблички я забил, там ничего обрабатываться не будет, соответственно и блокировать ничего не нужно.
     
    Последнее редактирование: 10 ноя 2024
  9. Автор темы
    normalped

    normalped Участник Пользователь

    Баллы:
    36
    Да в принципе ничего. Можно подробнее?
     
  10. Автор темы
    normalped

    normalped Участник Пользователь

    Баллы:
    36
    Сделал как то так. По идеи, все работает, но есть нюанс: некоторые пакеты не импортируются. Нету доступа к серверным библиотекам и даже к своим классам из проекта. Я проверял этот способ в простом тестовом проекте который никак не связан с майнкрафтом и там такой фигни не было. Кто то знает как это решать?

    Код:
    MainTest.java:1: error: package org.jetbrains.annotations does not exist
     import org.jetbrains.annotations.NotNull;
    Код:
    public static String FOLDER = Plugin.instance().getDataFolder().toPath().resolve("test").toString();
    public static String STORAGE = "./storage";
    
    public static Test load(String name) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        File file = new File(FOLDER, name);
        Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(file);
        List<String> options = new ArrayList<>();
        options.add("-d");
        options.add(STORAGE);
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, compilationUnits);
        boolean result = task.call();
        try {
            fileManager.close();
            if (!result) throw new RuntimeException("Error");
            URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { new File(STORAGE).toURI().toURL() });
            Class<?> cl = Class.forName(name.replace(".java", ""), true, classLoader);
            return (Test) cl.getDeclaredConstructor().newInstance();
        } catch (IOException | ClassNotFoundException | InvocationTargetException | InstantiationException |
                 IllegalAccessException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
    
     
    Последнее редактирование: 13 ноя 2024
  11. Автор темы
    normalped

    normalped Участник Пользователь

    Баллы:
    36
    Короче, это решается добавлением опции -classpath для задачи компилятору. В classpath'е должны находится пути к вашему плагину и всем либам в папке libraries (которая находится в главной папке сервера).
     
  12. Dymeth

    Dymeth Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Dymeth
    ASM - библиотека для патчинга java кода, встроенная во все Paper ядра и его форки. Позволяет внедрять достаточно гибкие изменения. Технология Mixins от Sponge как раз работает на основе ASM.
    Впрочем, ты уже решил вопрос без её использования, и, возможно, это тоже нормальное и удобное решение.
    Но, опять же, встаёт вопрос безопасности.

    В чём потенциальная опасность? Ты ставишь чужой аддон для плагина, а он ворует пароли пользователей из AuthMe, например. Или сливает сборку создателю этого аддона. Или просто становишься частью ботнета.
    Было множество историй про верусню в аддонах PAPI, хотя казалось бы - там обычный JavaScript.
    Конечно, если ты не планируешь ставить чужие аддоны - проблемы не будет. Но жизнь непредсказуемая штука.
    Та же проблема с безопасностью касается и установки плагинов, но там:
    а) чаще можно найти исходный код
    б) на нормальных сайтах модераторы вручную проверяют загружаемый контент на вирусы

    В общем сам оценивай риски, моё дело просто предупредить.

    Так ты вынуждаешь любого человека, который ставит твой плагин, выполнять специфическую настройку сервера исключительно ради твоего плагина. Если плагин публичный - лучше этого избегать. На этот случай есть ещё два варианта:
    1) Указать в plugin.yml в секции libraries мавен-зависимости из Maven central, которые должны быть доступны в рантайме. Работает на Spigot 1.17 и новее
    2) Зашейдить нужные зависимости прямо в джарник плагина. Работает на всех версиях, однако увеличивает размер джарника. Ну и релокацию пакетов лучше не забывать, чтобы избежать конфликтов между классами с одинаковыми названиями, но из библиотеки разных версий

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

Поделиться этой страницей