Статьи

Теневые поля против интерфейса доступа к свойствам Раунд 3

Это третий раунд интерфейса « Теневые поля против средств доступа к свойствам» . Если вы новичок и не знаете, о чем идет речь, ознакомьтесь с моим предыдущим постом или моим первым постом на тему экономии памяти при разработке приложений JavaFX. Как разработчик Java, моей главной заботой является хороший баланс между производительностью , использованием памяти и снижением стандартного кода (простота использования API) при разработке моделей доменов JavaFX. Обычно платформы приложения предоставляют контроллер представления модели (MVC) или шаблон модели представления для отделения кода пользовательского интерфейса от объектов домена. На самом деле, что приходит на ум, это то, что объекты типа модели предметной области должны легко создаваться или генерироваться ( IDE ). В этой записи блога вы увидите результаты третьего раунда, состоящего из двух частей . Часть 1 была реализована с использованием идеи Марселя Хеккеля, а часть 2 — реализация, с которой я в конечном итоге остановился, основываясь на производительности , использовании памяти и простоте использования .

  • Отказ от ответственности: используйте любой код на свой страх и риск. Это чисто экспериментально и не должно использоваться в производстве. Работа в процессе.

Последний код здесь -> [ Интерфейс PropertyAccessors ]

Резюме 2 раунда

Хотя последний раунд ( раунд 2 ) показал, что моя стратегия Access Propertys была немного лучше в потреблении памяти, чем стандартная (толстая) стратегия объектов свойств, она все еще разочаровывала с точки зрения производительности при создании 2 000 000 объектов класса типа Employee с собственным Тип объектов . Я все еще не был доволен использованием памяти моей реализацией второго раунда по сравнению с реализацией Дирка. Если вам не безразличны мои окончательные результаты третьего раунда, просто перейдите в раздел « Результаты ».

Потому что может быть новый раунд, пожалуйста, проверьте текущий код здесь, если вы решите использовать его или предположите, что Дирк решит принять мой запрос на отправку прямо на его учетную запись Github здесь .

Во втором раунде я использовал поиск по хэш-карте, который может быть довольно медленным, так как все больше и больше полей добавляются с O (1) сложностью по времени (поиск) . Интересно, что Марсель Хеккель прокомментировал, предложив простой подход к созданию индексированного массива объектов, который не только сэкономит больше памяти, но и будет намного быстрее. По сравнению с поиском пары ключ / значение, прямой индексный доступ к полю определенно является подходящим. Хотя код Марселя работает быстрее, он все же занимает больше памяти, чем код Дирка Shadow Fields. Дополнительная память фактически занята предварительным выделением массива, который будет содержать значения для каждого поля. Даже если все они равны нулю, сам массив создается для каждого объекта сотрудника. Я реализовал стратегию Марселя здесь (строка 23). Давайте посмотрим на результаты массива индексированных полей стратегии.

Часть 1. Использование массива проиндексированных полей

1
2
3
4
5
private final Object[] modelProperties =
                               new Object[FIELDS.values().length];
    public Object[] getModelProperties(){
        return modelProperties;
    }

ТЕСТ: Объекты, не использующие поля свойств

Ниже показано использование идеи индексированного массива Марселя и моего способа указания имени свойства с использованием типов enum для обозначения полей как полей свойств.

скрин-шот-2016-04-07-в-12-18-01-ам

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

Выше вы заметите, что флажок снят, чтобы указать, что нельзя создавать свойства JavaFX для объекта домена ( не используя методы xxxxProperty () ). Вы заметите, что производительность значительно возросла по сравнению с кодом второго раунда, а также уменьшилось использование памяти. На рисунке выше интерфейс Property Accessor на 16 МБ больше, чем реализация шаблона Shadow Fields . В первой части производительности тонких объектов и использования памяти Shadow Fields является явным победителем. Однако Shadow Fields все еще не так чист для реализации. Следует также отметить, что интерфейс Property Accessors не использует 14 миллисекунд для 2 миллионов объектов! Как мы увидим позже в Части 2 Возвращение частных переменных экземпляра в виде полей, интерфейс Accessors свойств будет действительно сиять при использовании памяти.

ТЕСТ: Объекты, использующие поля свойств

Ниже приведены результаты, когда все поля объекта используют свойства JavaFX.

скрин-шот-2016-04-07-в-12-24-22-ам

Объекты, которые используют свойства JavaFX по сравнению со стандартными (толстыми) объектами со всеми полями в качестве свойств Javafx. Эта реализация использует индекс массива для каждого поля и массив для хранения каждого значения.

Здесь вы заметите, что столбцы Accessor (интерфейс Property Accessors) с 2 миллионами объектов работают за 916 миллисекунд с использованием 576 МБ памяти. В этом случае стандартный (толстый) объект является победителем, поскольку объем памяти составляет 544 МБ. До сих пор Shadow Fields выигрывает по производительности в каждом раунде.

Одна небольшая деталь в примере кода Марселя (в разделе комментариев) заключается в том, что он не учитывает строковое имя свойства при создании нового экземпляра объекта свойства. Например, следующий оператор показывает переменную totalProperty со свойством с именем « total », которое соответствует методу totalProperty () . Создание имени свойства во время изменения важно для чтения кода, тестирования и инструментов.

Свойство totalProperty = new SimpleIntegerProperty (this, «total», new Integer (5));

Чтобы иметь как именованное поле, так и индекс, как идея Марселя, я просто создал перечисление, объявляющее каждое свойство поля. Эти перечисления создаются в классе Employee .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
// Part 1 implementation
public class EmployeePropertyAccessor implements PropertyAccessors{
    public enum FIELDS {
        name,
        powers,
        supervisor,
        minions
    }
    private final Object[] modelProperties =
                               new Object[FIELDS.values().length];
 
    public Object[] getModelProperties(){
        return modelProperties;
    }
 
    // The rest of the code...

Выше вы заметите, как будет создан массив свойств модели на основе количества определенных полей (перечисление FIELDS). Я использую FIELDS.value (). Length, чтобы определить размер массива. Кроме того, интерфейс PropertyAccessors (реализация части 1 ) заставляет разработчика реализовать метод getModelProperties () . Здесь я только что вернул ссылку modelProperties на массив объектов. Не очень приятно «реализовывать» массив и метод getModelProperties () .

Во второй части этой статьи я реализовал разные вещи, когда разработчик не обязан реализовывать массив modelProperties и метод getModelProperties () . Я решу эту проблему, когда код будет выглядеть чище и эффективнее (с точки зрения пользователя API).

Часть 2: Повторное представление переменных экземпляра

Во второй части я добавлю частные переменные экземпляра обратно в класс Employee ( EmployeePropertyAccessor ) для хранения значений поля вместо массива, как в части 1. Моя идея состояла в том, чтобы переменная поля была взаимоисключающей, чтобы либо указывать на собственный объект type или свойство JavaFX, тем самым экономя память по сравнению с кодом шаблона Shadow Field. Поскольку в коде теневых полей используются две переменные для представления значения поля, у него будет дополнительная ссылка, которая неизбежно увеличит его память, когда объект использует свойства. Как вы можете видеть ниже, код будет похож на часть 1, но также будет иметь статический блок для регистрации полей свойств в классе. Это важно, потому что некоторые переменные экземпляра, которые вы не хотите участвовать в качестве свойств JavaFX.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Part 2 implementation
public class EmployeePropertyAccessor implements PropertyAccessors {
 
    private Object name;
    private Object powers;
    private Object supervisor;
    private Object minions;
 
    enum FIELDS {
        name,
        powers,
        supervisor,
        minions
    }
 
    static {
        // register fields one time.
        // (Warning: enum's ordinal value is reassigned an index number)
        registerFields(EmployeePropertyAccessor.class, FIELDS.values());
    }
 
    public EmployeePropertyAccessor(String name, String powers) {
        setName(name);
        setPowers(powers);
    }
 
    public final String getName() {
        return getValue(FIELDS.name, "");
    }
    public final void setName(String name) {
        setValue(FIELDS.name, name);
    }
    public final StringProperty nameProperty() {
        return refProperty(FIELDS.name,
SimpleStringProperty.class, String.class);
    }
 
    // The rest of the code...

Приведенный выше листинг кода вызывает интересную магию при вызове метода registerFields () . Порядковые значения перечислений FIELDS переназначаются с использованием отражения, давая каждому новый идентификатор в качестве индекса в массиве. Это обеспечивает неизменяемые перечисления, а также содержит уникальный идентификатор для каждого поля, к которому можно быстро получить доступ через индекс. Поскольку enum используются для представления полей, которые будут использоваться в качестве свойств, порядковые значения не имеют смысла в других контекстах. Это означает: кого это волнует, если порядковый номер переназначен для этих объявленных перечислений? Они используются только для этой цели « для регистрации полей ».

ТЕСТ: Объекты, не использующие поля свойств [NEW]

Ниже показаны результаты теста с использованием новой реализации API интерфейса Property Accessors. Тест ниже показывает, когда не используются поля свойств по сравнению со стандартным толстым объектом.

Тест, сравнивающий стандартные объекты (все поля, использующие свойства) с объектами, использующими собственные объекты. API интерфейса Access Accessors теперь использует переменные экземпляра в качестве полей вместо ссылки на массив.

Тест, сравнивающий стандартные объекты (все поля, использующие свойства) с объектами, использующими собственные объекты. API интерфейса Access Accessors теперь использует переменные экземпляра в качестве полей вместо ссылки на массив.

Как вы можете видеть выше, новая реализация интерфейса Property Accessors — явный победитель в использовании памяти и простоте использования. Производительность немного медленнее, чем в первой части, но экономия памяти того стоит. Вы заметите, что использование памяти Shadow Fields на 16 МБ больше, чем использование Accessors.

TEST: объекты, использующие поля свойств [NEW]

Ниже показаны результаты теста с использованием новой реализации API интерфейса Property Accessors. Тест ниже показывает, когда используются поля свойств по сравнению со стандартным толстым объектом. (Флажок установлен под кнопкой Пуск)

скрин-шот-2016-04-09-в-12-36-02-м

Результаты 3 тура

Ниже приведены гистограммы, которые я составил вместе на основе результатов в таблицах. Я чувствовал, что людям нравится видеть диаграммы вместо таблиц, ячеек и текста.

performance_a

Результаты тестирования производительности, когда объекты не используют свойства JavaFX. Меньшее число (в миллисекундах) лучше.

performance_b

Результаты тестирования производительности, когда объекты используют свойства JavaFX. Меньшее число (в миллисекундах) лучше.

memusage_a

Результаты тестирования использования памяти, когда объекты не используют свойства JavaFX. Меньшее число (в мегабайтах) лучше.

memusage_b

Результаты тестирования использования памяти, когда объекты используют свойства JavaFX. Меньшее число (в мегабайтах) лучше.

Вывод

Основываясь на результатах, моя цель была определенно достигнута (IMHO), где я изначально хотел, чтобы код был простым для чтения и простым для реализации, когда объекты могут использовать или не использовать свойства JavaFX ( с дополнительным преимуществом экономии памяти, когда поля не используют JavaFX свойства [нативные типы] ). Несмотря на то, что Shadow Fields выиграл во всех тестах, интерфейс Property Accessors не сильно отстал. Если не использовать свойства, интерфейс Property Accessors превосходит стандартную стратегию объекта всего за 5 миллисекунд при создании 2 миллионов записей.

Когда речь идет об использовании памяти при создании 2 миллионов объектов и когда в стратегиях не используются свойства в качестве полей, интерфейс Property Accessors явно выигрывает с экономией не менее 16 МБ по сравнению с шаблоном теневых полей и 240 МБ по сравнению с Код стандартных свойств. Наконец, что не менее важно, это результаты, когда объекты используют свойства в качестве полей, а интерфейс Accessors связывается со стандартной стратегией объектов в отношении потребления памяти. Стратегия Shadow Fields использует по крайней мере на 20 МБ больше, чем другие стратегии.

Несмотря на то, что интерфейс Properties Accessors немного медленнее (с небольшой разницей в миллисекундах) при использовании или не использовании всех полей в качестве свойств для 2 миллионов объектов, я убежден, что API можно использовать с любым размером приложения JavaFX для простоты разработки домена модели и объекты. Я призываю других самостоятельно протестировать код, прежде чем принимать решение об использовании API. Обращаем ваше внимание, что код не считается готовым к работе и является очень экспериментальным. Эта работа еще не завершена, поэтому пока я не поеду свою собачью еду (на работе), я не могу порекомендовать вам использовать API-интерфейс Property Accessors API. Я повторю заявление об отказе ниже:

  • Отказ от ответственности: используйте любой код на свой страх и риск. Это чисто экспериментально и не должно использоваться в производстве. Работа в процессе.

Не стесняйтесь комментировать и подписаться. Наслаждайтесь и счастливого кодирования!