Слюни запрос плохо масштабируется - PullRequest
1 голос
/ 06 июня 2019

Я использую Drools для вычисления баллов в своем проекте OptaPlanner, и после того, как я начал использовать запросы для извлечения логических фактов из рабочей памяти в Java, я понял, что по мере увеличения размера ввода запросы выполнялись дольше (они не хорошо масштабируется).

Логические факты, которые я извлекаю, накапливаются из множества ограничений, и я использую их в Java-части приложения, цель состоит в том, чтобы повторно использовать то, что слюни уже рассчитали, и извлечь эти значения в O (1 ) время.

Примечание. Используемая мной версия Drools и OptaPlanner - 7.0.0-SNAPSHOT.

Воспроизведение проблемы с простым приложением Drools

Чтобы представить проблему масштабирования, которую я обнаружил, я упростил вещи и создал отдельный проект простых слюней, который содержит только один основной класс для выполнения приложения и два других класса, предназначенных для фактов слюней. Это также очевидно позволяет избежать накладных расходов, добавляемых OptaPlanner.

Как я уже говорил в этом простом проекте, есть два класса, предназначенных для фактов: Employee , в котором есть свойство "id", и TotalHours , в котором есть "employee" и "totalHours" как свойства.

public class Employee {
    public int id;
}

public class TotalHours {
    public Employee employee;
    public int totalHours;
}

Запрос в этом проекте сопоставляет сотрудника, который передается в качестве параметра в вызове, с частью сотрудника объекта TotalHours. Обратите внимание, что для данного сотрудника может быть максимум один объект TotalHours (ноль или один).

query "get TotalHours for Employee"  (Employee e)
   totalHours : TotalHours(employee == e)
end

Проблема масштабирования присутствует независимо от того, создаю ли я объекты TotalHours в части Java и вставляю их в рабочую память вместе с объектами Employee или пишу правило, которое на основе объектов Employee будет создавать логические факты. Как я уже упоминал ранее, факты в моем проекте OptaPlanner факты, которые получены по запросу, являются логическими фактами.

Таким образом, очевидно, что запрос не масштабируется, как, например, когда правило пытается сопоставить два шаблона с их общим свойством, как в случае ниже. Даже в случае, когда отношение между Employee и TotalHours равно 1: N, это означает, что сотрудник может сопоставить несколько объектов TotalHours, приложению потребуется приблизительно одинаковое количество времени для выполнения правила, независимо от того, увеличивается ли размер сотрудника. Это будет хорошо масштабироваться из-за хеширования.

rule "Match Employee and TotalHours"
    when
        $employee : Employee()
        TotalHours(employee == $employee)
    then
end

Измерение производительности

Я делаю два типа тестов для измерения масштабируемости простого приложения drools. В обоих случаях в зависимости от размера теста создаются объекты N Employee и TotalHours, которые вставляются в рабочую память и вызывается «fireAllRules». Тогда в:

Тест 1 - скорость запроса проверяется при увеличении N

  1. Запрос вызывается 1000 раз для сотрудников, выбранных случайным образом
  2. Измеряется время, необходимое для выполнения этих 1000 запросов с различными размерами N

    double totalTime = 0L;
    for (int i = 0; i < testSize; i++) {
        int randomIndex = random.nextInt(employees.size());
        Employee employee = employees.get(randomIndex);
        long startTime = System.currentTimeMillis();
        QueryResults queryResults = kSession.getQueryResults("get TotalHours for Employee", employee);
        long endTime = System.currentTimeMillis();
        TotalHours totalHours = (TotalHours) queryResults.iterator().next().get("totalHours");
        totalTime += (endTime - startTime);
    }
    

Тест 2 - Скорость правила «Match Employee and TotalHours» проверяется при увеличении N

  1. Свойство totalHours в TotalHours обновляется, и fireAllRules вызывается 1000 раз
  2. Измеряется время, необходимое для обновления переменной и выполнения в различных размерах N

    double totalTime = 0L;
    for (int i = 0; i < testSize; i++) {
        int randomIndex = random.nextInt(totalHoursList.size());
        TotalHours totalHours = totalHoursList.get(randomIndex);
        int randomTotalHours = random.nextInt(100000);
        long startTime = System.currentTimeMillis();
        totalHours.setTotalHours(randomTotalHours);
        FactHandle factHandle = kSession.getFactHandle(totalHours);
        kSession.update(factHandle, totalHours);
        kSession.fireAllRules();
        long endTime = System.currentTimeMillis();
        totalTime += (endTime - startTime);
    }
    

Я измерил объекты 1000, 5000, 10000, 50000, 100000 и 500000 Employee и TotalHours, вставленные в рабочую память. В приведенных ниже результатах мы видим среднее время (в миллисекундах), необходимое для завершения запроса или выполнения правила (среднее из 1000 случайных испытаний).

Неуклонное увеличение среднего времени, затрачиваемого на выполнение запроса, можно увидеть по мере увеличения числа сотрудников, в то время как мы видим, что правило, сопоставляющее Employee с TotalHours, работает постепенно на основе результатов. . Results

скриншот белВзятый из Java Flight Control Flight Recording показывает, где большую часть времени проводят, и, как мы видим, 99,63% времени тратится внутри метода "org.drools.core.phreak.PhreakJoinNode.doLeftInserts".Call Tree from Flight Recording on version 7.0.0-SNAPSHOT

Кстати, я позаботился о том, чтобы сборка мусора не отвлекала в этих тестах, и я просто установил начальный и максимальный размер кучи равным 6 ГБ, где самый большой тест был выполнен здесь (500000 сотрудников) требуется около 700 МБ для запуска.CPU and memory usage

Проблема по-прежнему существует в последней версии Drools (7.22.0. Окончательно на этом этапе)

Мне было любопытно узнать, решена ли эта проблемаЗабота о самой новой версии Drools и, исходя из полученных результатов, все еще ведет себя так же, все еще плохо масштабируется.Я скачал самую новую версию Drools (на данный момент 7.22.0.Final) и выполнил тот же тест запросов, о котором говорилось выше.

Мы можем видеть результаты двух версий бок о бок.Не обманывайтесь небольшим увеличением среднего времени, затрачиваемым в новой версии, потому что при запуске тестов на моем компьютере было больше накладных расходов.Results from query test 7.0.0-SNAPSHOT vs 7.22.0.Final

Также на основе этого снимка экрана, взятого из полета управления полетом Java. Горячей точкой по-прежнему является «org.drools.core.phreak.PhreakJoinNode.doLeftInserts», хотяСтек выглядит немного иначе.Call Tree from Flight Recording on version 7.22.0.Final

Помимо всего прочего, я ищу эффективный O (1) способ получения того, что Drools уже рассчитал, и возможности использовать эту информацию в Javaсторона заявления.

...