Более внимательный подход к разработке программного обеспечения

Когда разработчики программного обеспечения задумываются о том, как выглядит хороший дизайн программного обеспечения при работе с высокоуровневыми языками программирования, многие умы инстинктивно приходят к руководящим принципам объектно-ориентированного программирования (ООП).

Мы так сильно опираемся на эту парадигму не только потому, что большинство из нас учились так, но и потому, что объектно-ориентированный подход - структурирование кода на основе объектов в нашей воспринимаемой модели реального мира - это интуитивно понятный и в целом хороший способ разработки программного обеспечения при работе с объектно-ориентированными языками программирования. Инкапсуляция, абстракция, наследование и полиморфизм - четыре очень важных принципа в разработке программного обеспечения, которые прекрасно работают в рамках объектно-ориентированной парадигмы, обеспечивая при этом высокий уровень повторного использования кода и ремонтопригодность. Достоинства объектно-ориентированного программирования многочисленны и хорошо документированы.

Хотя в объектно-ориентированном дизайне по сути нет ничего плохого, разработчики могут запрограммировать себя в коробку, потому что они ограничили себя размерами объектно-ориентированного мира. Код, который хорошо смоделирован с объектно-ориентированной точки зрения, может фактически замаскировать тот факт, что код не решает проблему оптимальным образом, игнорируя в первую очередь цель написания кода. Использование подхода, основанного на том, как код действительно работает для преобразования данных программы - подход, ориентированный на данные - может помочь программистам избежать ловушек, которые может скрыть объектно-ориентированный код.

Введение в дизайн, ориентированный на данные (DOD)

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

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

Типичным примером этого является то, как программисты пишут свой код без учета того, как их программа использует кэш ЦП. Кэш-память позволяет компьютеру использовать локальность данных для значительного повышения эффективности доступа к памяти; однако, если программист не пишет свою программу с учетом локальности данных, он упускает этот ключевой прирост производительности.

Дизайн, ориентированный на данные, - это идея написания кода для создания структур данных, оптимизированных для эффективной обработки, а не написания кода для структурирования данных вокруг реальных объектов. Это означает, что данные и функциональность разделены, чтобы функции могли действовать в отношении данных в более общем виде. Это упрощает оптимизацию производительности за счет использования кэша и распараллеливания.

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

Дизайн, ориентированный на данные: пример на C ++

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

В этом примере рассматриваются две разные реализации простой системы управления портфелем акций на C ++. Первый фрагмент кода показывает структуру класса, соответствующую принципам объектно-ориентированного проектирования.

Ключевым элементом данных в этом классе является портфель акций, который состоит из вектора структур позиций акций. Каждая позиция по акциям состоит из символа акции, текущей стоимости акции, количества принадлежащих акций и средней стоимости, уплаченной за каждую акцию. Моделирование портфеля акций в виде списка моделей позиций акций - очень интуитивный способ структурировать этот код. Однако выполнение вычислений в этой структуре данных требует циклического просмотра портфеля акций и доступа к 56-байтовому блоку памяти для каждой позиции акции, даже если для вычислений требуется лишь небольшая часть информации о каждой позиции акции.

В качестве альтернативы, ориентированный на данные дизайн системы портфеля акций может выглядеть примерно так:

Ключевое отличие в этом примере заключается в том, что вместо массива структур (AoS), как в первом примере, этот класс использует структуру массивов (SoA). для хранения всех данных, связанных с портфелем акций. Это позволяет нам выполнять вычисления, которые перебирают массивы меньших элементов данных, которые можно более эффективно кэшировать. Это может значительно повысить скорость работы приложения, в котором используется этот класс.

Как это влияет на производительность вычислительных методов?

Реализация вычислительных методов в объектно-ориентированной версии может выглядеть так:

Между тем реализация вычислительных методов в ориентированной на данные версии может выглядеть так:

Когда общий доход рассчитывается для портфеля акций, состоящего из 1500 позиций акций, реализация, ориентированная на данные, превосходит объектно-ориентированную реализацию в среднем в 2 раза (GCC 7.5.0; Ubuntu 18.04 64 -бит; g ++ -O2). Даже для такого небольшого фрагмента кода изменение структуры кода привело к значительно более эффективной системе, поскольку оборудование имело более прямой доступ к данным. Основываясь на этом примере, легко увидеть, как объектно-ориентированный код, написанный без учета базового оборудования, может привести к неполноценной системе, даже если структура кода кажется разумной.

Да, DOD и OOP могут (и должны) сосуществовать

Это связано с тем, что дизайн, ориентированный на данные, и объектно-ориентированное программирование не исключают друг друга. Дизайн, ориентированный на данные, можно рассматривать как философию программирования, а не как конкретную структуру дизайна для кода. Для него характерно программирование, в котором основное внимание уделяется решению проблемы преобразования данных, прежде чем даже рассматривать дизайн кода. Как только программист решил, как оборудование должно взаимодействовать с данными, код может быть разработан на основе этих данных.

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

Заключение

При написании кода с помощью языков программирования высокого уровня легко потеряться в удобных уровнях абстракции, предоставляемых нам, и это приводит к тому, что мы забываем, что наш код отвечает за указание оборудованию, как преобразовывать данные очень специфическим образом на нижний уровень. Философия, ориентированная на данные, приводит к более основательному коду и заставляет разработчиков по-настоящему понимать, что делает их язык программирования под капотом. Сознательное отношение к проектированию, ориентированному на данные, не может радикально изменить способ написания кода, но лучшее понимание того, почему это важно, почти наверняка сделает вас лучшим разработчиком.

Спасибо за прочтение! Я также хочу поблагодарить всех участников моего курса Расширенные темы в C ++ за просмотр первого черновика этой статьи. Ваш отзыв очень помог при создании финальной версии.

Если вы хотите узнать больше о дизайне, ориентированном на данные, я настоятельно рекомендую ознакомиться с некоторыми из приведенных ниже ресурсов.

Дополнительные ресурсы