﻿# Вторая часть исследования Nau Engine

Во второй части нашей трилогии об игровом движке Nau Engine мы обсудим важные аспекты оптимизации и повышения производительности\. Наша цель — выявить проблемы, которые могут повлиять на эффективность и стабильность игр, созданных с использованием Nau Engine\.

![1226_NauEngineTwo_ru/image1.png](https://import.viva64.com/docx/blog/1226_NauEngineTwo_ru/image1.png)

[В первой части](https://pvs-studio.ru/ru/blog/posts/cpp/1221/) мы сосредоточились на функциональности Nau Engine, разобрав три категории ошибок: проблемы с памятью, копипасту и логические ошибки\. Однако, помимо этих аспектов, не менее важную роль играет и производительность\. Давайте посмотрим на результаты проверки с помощью PVS\-Studio\.

**Фрагмент N1**

```cpp
std::vector<AnimationTargetData> m_targets;

void AnimationComponent::addAnimationTarget(IAnimationTarget::Ptr target)
{
  if (target)
  {
    if (auto* nauObject = target->as<scene::NauObject*>())
    {
      ....
      m_targets.push_back(AnimationTargetData(std::move(wrapper), nullptr));
    }
    else
    {
      m_targets.push_back(AnimationTargetData(std::move(target), nullptr));
    }
  }
}
```

Предупреждение PVS\-Studio: [V823](https://pvs-studio.ru/ru/docs/warnings/v823/) Decreased performance\. Object may be created in\-place in the 'm\_targets' container\. Consider replacing methods: 'push\_back' \-\> 'emplace\_back'\. [animation\_component\.cpp 180](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/animation/src/components/animation_component.cpp#L180)

В коде используется `push_back` для добавления объектов в вектор `m_targets`, что приводит к созданию временного объекта, который затем копируется или перемещается в контейнер\. Это приводит к лишнему вызову конструктора перемещения/копирования\. Чтобы создать объект непосредственно в контейнере без промежуточных шагов, лучше заменить `push_back` на `emplace_back`\. Использование `emplace_back` позволяет передать аргументы конструктора в вектор, создавая объект "по месту" и избегая дополнительных накладных расходов\.

**Фрагмент N2**

```cpp
void writeContainerHeader(....)
{
    ....
    const eastl::string contentLength = eastl::to_string(serializedData.size());

    Vector<eastl::tuple<eastl::string, eastl::string>> httpHeader = {
        {"NauContent-Kind", eastl::string(kind)},
        {"Content-Type", "application/json"},
        {"Content-Length", std::move(contentLength)}
    };
    ....
}
```

Предупреждение PVS\-Studio: [V833](https://pvs-studio.ru/ru/docs/warnings/v833/) Passing the const\-qualified object 'contentLength' to the 'std::move' function disables move semantics\. [nau\_container\.cpp 68](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/src/io/nau_container.cpp#L68)

Объект `contentLength` типа `eastl::string` объявлен с квалификатором `const`\. Затем разработчик захотел переместить его внутрь вектора `httpHeader` и применил для этого `std::move`\. К сожалению, перемещения не произойдёт, так как не будет выбрана [перегрузка](https://github.com/electronicarts/EASTL/blob/master/include/EASTL/string.h#L514) конструктора `eastl::string` с rvalue\-ссылкой\. Вместо этого, по правилам выбора перегрузок, предпочтение будет отдано [конструктору копирования](https://github.com/electronicarts/EASTL/blob/master/include/EASTL/string.h#L507)\.

Решение проблемы очень простое: убрать квалификатор `const` у `contentLength`, и тогда объект сможет быть перемещён\.

Однако это не всё, в этом коде есть ещё одно неявное копирование строк\. При объявлении `httpHeader` вызывается конструктор от [`std::initializer_list`](https://github.com/electronicarts/EASTL/blob/master/include/EASTL/string.h#L512)\. Последний представляет собой легковесный прокси\-объект над массивом типа `const T`\. В итоге компилятор представит код примерно следующим образом:

```cpp
void writeContainerHeader(....)
{
    ....
    eastl::string contentLength = eastl::to_string(serializedData.size());

    const eastl::tuple<eastl::string, eastl::string> backing_array[] {
        {"NauContent-Kind", eastl::string(kind)},
        {"Content-Type", "application/json"},
        {"Content-Length", std::move(contentLength)}      
    };

    Vector<eastl::tuple<eastl::string, eastl::string>> httpHeader {
      std::initializer_list { backing_array }
    };
    ....
}
```

Исходя из этого константные кортежи будут копироваться в вектор, а это приведёт к копированию строк внутри константного массива\. Избежать этого можно, если отказаться от `std::initializer_list` в пользу вызовов `emplace_back`:

```cpp
void writeContainerHeader(....)
{
    ....
    eastl::string contentLength = eastl::to_string(serializedData.size());

    Vector<eastl::tuple<eastl::string, eastl::string>> httpHeader;
    httpHeader.reserve(3);

    httpHeader.emplace_back("NauContent-Kind", eastl::string(kind));
    httpHeader.emplace_back("Content-Type", "application/json");
    httpHeader.emplace_back("Content-Length", std::move(contentLength));
}
```

Такой код будет более производительным, но ценой лаконичности\.

**Фрагмент N3**

```cpp
inline const ComponentInfo getEntityComponentInfo(....) const
{
  EntityComponentRef ref = getEntityComponentRef(eid, cid);
  if (ref.isNull())
    return ComponentInfo("<invalid>", eastl::move(ref));
  return ComponentInfo(dataComponents
                       .getComponentNameById(ref.getComponentId()),
                       eastl::move(ref));
}
```

Предупреждение PVS\-Studio: [V839](https://pvs-studio.ru/ru/docs/warnings/v839/) The 'EntityManager::getEntityComponentInfo' function returns a constant value\. This may interfere with move semantics\. [entityManager\.h 1855](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/dagorECS/include/daECS/core/entityManager.h#L1855)

Функция `getEntityComponentInfo` возвращает объекты типа `const ComponentInfo`\. Такое написание возвращаемого типа считается устаревшим \(CppCoreGuidelines [**F\.49**](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-const)\), и до C\+\+17 может привести к лишнему копированию, когда объект инициализируется результатом вызова функции\.

Решение проблемы простое: убрать `const` из возвращаемого типа\.

```cpp
inline ComponentInfo getEntityComponentInfo(....) const
{
  ....
}
```

И вот ещё случаи:

* V839 The 'getCommit' function returns a constant value\. This may interfere with move semantics\. [engine\_version\.h 54](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/include/nau/version/engine_version.h#L54)
* V839 The 'getBranch' function returns a constant value\. This may interfere with move semantics\. [engine\_version\.h 59](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/include/nau/version/engine_version.h#L59)
* V839 The 'getFullPathCache' function returns a constant value\. This may interfere with move semantics\. [CCFileUtils\.h 831](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/platform/CCFileUtils.h#L831)
* V839 The 'FileUtils::getSearchResolutionsOrder' function returns a constant value\. This may interfere with move semantics\. [CCFileUtils\.cpp 971](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/platform/CCFileUtils.h#L971)
* V839 The 'FileUtils::getSearchPaths' function returns a constant value\. This may interfere with move semantics\. [CCFileUtils\.cpp 977](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/platform/CCFileUtils.h#L977)
* V839 The 'FileUtils::getOriginalSearchPaths' function returns a constant value\. This may interfere with move semantics\. [CCFileUtils\.cpp 983](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/platform/CCFileUtils.h#L983)
* V839 The 'FileUtils::getDefaultResourceRootPath' function returns a constant value\. This may interfere with move semantics\. [CCFileUtils\.cpp 995](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/cocos2d-x/cocos/platform/CCFileUtils.h#L995)
* V839 The 'ProgramNau::getActiveAttributes' function returns a constant value\. This may interfere with move semantics\. [program\_nau\.cpp 99](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/src/nau_backend/program_nau.cpp#L99)

**Фрагмент N4**

```cpp
eastl::unique_ptr<uint8_t[]> convertData(....)
{
  switch (format)
  {
  case cocos2d::backend::PixelFormat::RGBA4444:
  {
    ....
    eastl::unique_ptr<uint8_t[]> newData{ new uint8_t[....] };

    ....
    return std::move(newData);
  }
  ....
}
```

Предупреждение PVS\-Studio: [V828](https://pvs-studio.ru/ru/docs/warnings/v828/) Decreased performance\. Moving a local object in a return statement prevents copy elision\. [texture\_nau\.cpp 78](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/ui/src/nau_backend/texture_nau.cpp#L78)

В этом фрагменте кода мы имеем дело с функцией, которая возвращает объект типа `eastl::unique_ptr<uint8_t[]>`\. Когда возвращаемый тип функции совпадает с типом возвращаемого значения, и это значение является локальной переменной, компилятор может выполнить одну из следующих операций:

* [Named Return Value Optimization \(NRVO\)](https://pvs-studio.ru/ru/blog/terms/6516/?ysclid=m71mxc76x94115751) — объект может быть создан непосредственно в месте вызова функции, что позволяет избежать любых перемещений или копирований;
* применение конструктора перемещения — если NRVO не может быть применён, компилятор может использовать перемещение, чтобы передать владение объектом;
* применение конструктора копирования — в случае, если ни NRVO, ни перемещение не могут быть использованы, будет применен конструктор копирования\.

Когда мы применяем `std::move`, выражение в `return` становится отличным от возвращаемого типа функции, что предотвращает возможность применения NRVO\. В результате код оказывается менее эффективным, так как компилятор может использовать либо перемещение, либо копирование вместо более оптимального NRVO\.

Таким образом, выражение `std::move(newData)` ведёт к пессимизации, а поэтому вызов функции `std::move` стоит удалить\.

**Фрагмент N5**

```cpp
void configureVirtualFileSystem()
{
  ....
  fs::path currentPath = fs::current_path();
  auto& props = getServiceProvider().get<GlobalProperties>();

  if (auto contentPath = props.getValue<eastl::string>("contentPath");
      contentPath)
  {
    const std::filesystem::path path = contentPath->c_str();
#ifdef NAU_PACKAGE_BUILD
    for (const auto& entry : fs::directory_iterator(path))
    {
      if (   entry.is_regular_file()
          && entry.path().extension() == ".assets")
      {
        const auto& filePath = entry.path();
        auto assetPackFS = io::createAssetPackFileSystem(
          strings::toU8StringView(filePath.string())
        );
        vfs.mount("/packs", assetPackFS).ignore();
        assetDb.addAssetDB("packs/assets_database/database.db");
      }
    }
#else
    auto contentFs = io::createNativeFileSystem(path.string());
    vfs.mount("/content", std::move(contentFs)).ignore();

    auto assetDbPath = path.parent_path() / "assets_database";
    vfs.mount("/assets_db", std::move(contentFs)).ignore();

    assetDb.addAssetDB("assets_db/database.db");
#endif
  }
}
```

Предупреждение PVS\-Studio: [V808](https://pvs-studio.ru/ru/docs/warnings/v808/) 'currentPath' object of 'path' type was created but was not utilized\. [default\_application\_delegate\.cpp 101](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/app_framework/src/app/default_application_delegate.cpp#L101)

Анализатор обнаружил, что переменная `currentPath`, созданная для хранения текущего пути с помощью `fs::current_path()`, не используется в дальнейшем коде функции `configureVirtualFileSystem()`\. Возможно, что после рефакторинга кода локальная переменная перестала использоваться, и её можно удалить\. Это никак не повлияет на логику функции\.

**Фрагмент N6**

```cpp
class ThreadPoolExecutor final : public Executor,
                                 public IRuntimeComponent
{
public:
  ThreadPoolExecutor(std::optional<size_t> threadsCount)
  {
    const size_t maxThreads = threadsCount ? *threadsCount
                                           : getDefaultThreadsCount();
    m_threads.reserve(maxThreads);

    for (size_t i = 0; i < maxThreads; ++i)
    {
      m_threads.emplace_back([](ThreadPoolExecutor& executor,
                                size_t threadIndex)
      {
        threading::setThisThreadName(
          std::format("Nau Pool-{}", threadIndex + 1)
        );
        executor.threadWork();
      }, std::ref(*this), i);
    }

    RuntimeObjectRegistration{nau::Ptr<>{this}}.setAutoRemove();
    void();
  }
  ....
};
```

Предупреждение PVS\-Studio: [V607](https://pvs-studio.ru/ru/docs/warnings/v607/) Ownerless expression 'void \(\)'\. [thread\_pool\_executor\.cpp 60](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/src/async/thread_pool_executor.cpp#L60)

В конструкторе класса `ThreadPoolExecutor` создаются рабочие потоки\. И в конце, как вишенка на торте, тело конструктора украшает конструкция `void();`, которая не выполняет никакой полезной работы\. Трудно сказать, почему она там появилась\. Возможно, в результате неаккуратного рефакторинга, или на месте этой конструкции должно быть что\-то другое\. 

**Фрагмент N7**

```cpp
struct ScheduledArchetypeComponentTrack
{
  ....
  ScheduledArchetypeComponentTrack() {}
  ....
};
```

Предупреждение PVS\-Studio: [V832](https://pvs-studio.ru/ru/docs/warnings/v832/) It's better to use '\= default;' syntax instead of empty constructor body\. [ecsQueryInternal\.h 35](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/dagorECS/src/core/ecsQueryInternal.h#L35)

Структура `ScheduledArchetypeComponentTrack` имеет определённый пользователем конструктор\. Однако лучше объявить его по умолчанию: в таком случае класс будет [тривиально конструируемым](https://en.cppreference.com/w/cpp/language/classes#Properties_of_classes)\. На основании этого компилятор может генерировать более оптимизированный код\. Более того, некоторые алгоритмы могут выбирать \([бенчмарк](https://quick-bench.com/q/n4rr6iybuHAOsBnTpwUmnPAAOh8)\) другую, более быструю стратегию при работе с тривиально конструируемыми объектами по умолчанию\.

Улучшенный код:

```cpp
struct ScheduledArchetypeComponentTrack
{
  ....
  ScheduledArchetypeComponentTrack() = default;
  ....
};
```

И вот ещё случаи:

* _V832 It's better to use '\= default;' syntax instead of empty constructor body\. [dag\_drvDecl\.h 149](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/render/include/nau/3d/dag_drvDecl.h#L149)_
* _V832 It's better to use '\= default;' syntax instead of empty constructor body\. [frustumClusters\.h 79](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/graphics/src/render/lights/frustumClusters.h#L79)_
* _V832 It's better to use '\= default;' syntax instead of empty constructor body\. [dag\_hlsl\_floatx\.h 17](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/graphics/include/nau/render/dag_hlsl_floatx.h#L17)_
* _V832 It's better to use '\= default;' syntax instead of empty constructor body\. [dag\_hlsl\_floatx\.h 38](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/graphics/include/nau/render/dag_hlsl_floatx.h#L38)_
* _V832 It's better to use '\= default;' syntax instead of empty constructor body\. [dag\_hlsl\_floatx\.h 60](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/graphics/include/nau/render/dag_hlsl_floatx.h#L60)_
* _V832 It's better to use '\= default;' syntax instead of empty constructor body\. [dag\_hlsl\_floatx\.h 87](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/graphics/include/nau/render/dag_hlsl_floatx.h#L87)_
* _V832 It's better to use '\= default;' syntax instead of empty constructor body\. [dag\_hlsl\_floatx\.h 104](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/graphics/include/nau/render/dag_hlsl_floatx.h#L104)_
* _V832 It's better to use '\= default;' syntax instead of empty constructor body\. [dag\_hlsl\_floatx\.h 122](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/graphics/include/nau/render/dag_hlsl_floatx.h#L122)_
* _V832 It's better to use '\= default;' syntax instead of empty destructor body\. [lowLatencyStub\.cpp 47](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/render/src/dagor/lowLatencyStub.cpp#L47)_

**Фрагмент N8**

```cpp
if (!this->hasComponent(depConstString.hash) 
&& (!optional || (optional && !can_skip_optional)))
{
  ....
}
```

Предупреждение PVS\-Studio: [V728](https://pvs-studio.ru/ru/docs/warnings/v728/) An excessive check can be simplified\. The '\|\|' operator is surrounded by opposite expressions '\!optional' and 'optional'\.  [dataComponent\.cpp 283](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/dagorECS/src/core/dataComponent.cpp#L283)

Выражение `(!optional || (optional && !can_skip_optional))` может быть упрощено\. Правая часть оператора `||` будет вычисляться лишь в том случае, если `optional` конвертируется в значение `true`\. Если это так, то левый операнд оператора `&&` всегда будет `true`\. Следовательно, проверка излишняя, и код можно упростить до следующего вида для повышения читабельности:

```cpp
if (!this->hasComponent(depConstString.hash) 
&& (!optional || !can_skip_optional)) 
{
  ....
}
```

**Фрагмент N9**

```cpp
// performance_profiling.h

static const nau::PerfTagFlag NAU_PERFTAGS = ....;
```

Предупреждение PVS\-Studio: [V1043](https://pvs-studio.ru/ru/docs/warnings/v1043/) A global object variable 'NAU\_PERFTAGS' is declared in the header\. Multiple copies of it will be created in all translation units that include this header file\. [performance\_profiling\.h 23](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/include/nau/utils/performance_profiling.h#L23)

Объявление констант в заголовочном файле — нормальная операция на первый взгляд\. Однако дьявол кроется в деталях\. В C\+\+ объекты, объявленные как `const` в пространстве имён \(в том числе глобальном\), имеют [внутреннее связывание](https://pvs-studio.ru/ru/blog/terms/6506/)\. Когда заголовочный файл включается в несколько единиц трансляции, каждый из них будет иметь свою собственную копию этой константы, что может привести к увеличению размера исполняемого файла\.

Чтобы избежать проблем, можно использовать один из следующих подходов\.

**До C\+\+17**\. Нужно разбить объявление и определение этой константы\. Объявление константы производим в заголовочном файле, воспользовавшись спецификатором `extern`\. Фактическое определение константы переносим в файл реализации:

```cpp
// performance_profiling.h
extern const nau::PerfTagFlag NAU_PERFTAGS;  // Объявление

// performance_profiling.cpp
const nau::PerfTagFlag NAU_PERFTAGS = ....;  // Определение
```

**Начиная с C\+\+17**\. Объявить константу в заголовочном файле со спецификатором `inline`\. Таким образом, в программе будет существовать лишь одна версия этой константы:

```cpp
// performance_profiling.h
inline static const nau::PerfTagFlag NAU_PERFTAGS = ...;
```

**Фрагмент N10**

```cpp
std::string Paths::getAssetsPath() const
{
  if (m_paths.find("assets") == m_paths.end())
  {
      return "";
  }

  return m_paths.find("assets")->second;
}
```

Предупреждение PVS\-Studio: [V838](https://pvs-studio.ru/ru/docs/warnings/v838/) Temporary object is constructed during the call of the 'find' function\. Consider using an ordered associative container with heterogeneous lookup to avoid construction of temporary objects\. [file\_system\.cpp 421](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/tools/shared/src/file_system.cpp#L421)

Давайте для начала взглянем, как [объявлено](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/tools/shared/include/nau/shared/file_system.h#L59) поле `Paths::m_paths`:

```cpp
class SHARED_API Paths
{
  ....
private:
  ....
  std::map<std::string, std::string> m_paths;
};
```

Итак, поле `Paths::m_paths` — это [ассоциативный сортированный контейнер](https://en.cppreference.com/w/cpp/container/map)\. Его функция\-член [`std::map::find`](https://en.cppreference.com/w/cpp/container/map/find) принимает объект того же типа, что и ключ\. Это значит, что строковый литерал будет конвертироваться в `std::string`, а это может повлечь за собой динамическую аллокацию\.

Такой поиск называется гомогенным, то есть когда передаваемый тип и ключ внутри контейнера совпадают\. Начиная с C\+\+14, для ассоциативных сортированных контейнеров добавлен гетерогенный поиск, то есть передаваемый тип и ключ внутри контейнера могут не совпадать\. Это может давать прирост производительности, так как не приходится производить дополнительную конвертацию\.

Чтобы активировать гетерогенный поиск, нужно передать в `std::map` в качестве третьего шаблонного аргумента тип `std::less<>`:

```cpp
class SHARED_API Paths
{
  ....
private:
  ....
  std::map<std::string, std::string, std::less<>> m_paths;
};
```

Помимо этой оптимизации, можно также заметить, что объект по ключу `"assets"` ищут дважды по контейнеру, что выглядит весьма неоптимально\. Поэтому я бы предложил следующее исправление:

```cpp
std::string Paths::getAssetsPath() const
{
  auto it = m_paths.find("assets");
  if (it == m_paths.end()) return {};
  return it->second;
}
```

**Фрагмент N11**

```cpp
class DeferredRT
{
  ....
protected:
  ....
  StereoMode m_stereoMode = StereoMode::MonoOrMultipass;
  int m_numRt = 0, m_width = 0, m_height = 0;
  nau::string m_name;

  d3d::SamplerHandle m_defaultSampler;
  ResizableResPtrTex m_mrts[MAX_NUM_MRT] = {};
  ResizableResPtrTex m_depth;

  bool m_useResolvedDepth = false;
};

DeferredRT::DeferredRT(const char* name,
                       int w, int h,
                       StereoMode stereoMode,
                       unsigned msaaFlag,
                       int numRT,
                       const unsigned texFmt[MAX_NUM_MRT],
                       uint32_t depthFmt)
  : m_stereoMode(stereoMode)
{
  m_name = name;
  ....
}
```

Предупреждение PVS\-Studio: [V818](https://pvs-studio.ru/ru/docs/warnings/v818/) It is more efficient to use an initialization list 'm\_name\(name\)' rather than an assignment operator\. [deferredRT\.cpp 117](https://github.com/NauEngine/NauEnginePublic/blob/main/engine/core/modules/graphics/src/render/deferredRT.cpp#L117)

Что мы видим здесь: в классе `DeferredRT` поле `m_name` инициализируется в теле конструктора\. Однако делается это не самым оптимальным способом\. Прежде, чем поток управления попадёт в тело конструктора, исполняется список инициализации\. Если поле не указано в нём, то оно будет инициализировано по умолчанию\. Когда строка конструируется по умолчанию, наиболее часто происходит следующее:

* ставится пометка, что объект не содержит динамической аллокации \([Small String Optimization](https://pvs-studio.ru/ru/blog/terms/6658/)\);
* во внутренний буфер помещается [нуль\-терминал](https://pvs-studio.ru/ru/blog/terms/0088/) первым элементом\.

Затем в теле конструктора вызывается оператор `=`, который отбрасывает результат инициализации по умолчанию\. Чтобы исправить положение, достаточно проинициализировать поле в списке инициализации конструктора:

```cpp
DeferredRT::DeferredRT(const char* name,
                       int w, int h,
                       StereoMode stereoMode,
                       unsigned msaaFlag,
                       int numRT,
                       const unsigned texFmt[MAX_NUM_MRT],
                       uint32_t depthFmt)
  : m_stereoMode(stereoMode)
  , m_name(name)
{
  ....
}
```

И вот ещё случаи:

* V818 It is more efficient to use an initialization list 'm\_stream\(stream\)' rather than an assignment operator\. [nanim\_asset\_container\.cpp 29](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/asset_formats/src/animation/nanim_asset_container.cpp#L29)
* V818 It is more efficient to use an initialization list 'm\_stream\(stream\)' rather than an assignment operator\. [material\_asset\_container\.cpp 51](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/asset_formats/src/material/material_asset_container.cpp#L51)
* V818 It is more efficient to use an initialization list 'm\_stream\(shaderPackStream\)' rather than an assignment operator\. [shader\_asset\_container\.cpp 60](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/modules/asset_formats/src/shader/shader_asset_container.cpp#L60)
* V818 It is more efficient to use an initialization list 'c\(p\)' rather than an assignment operator\. [dag\_bounds3\.h 209](https://github.com/NauEngine/NauEnginePublic/blob/020cbfec7bd4cff7ff4303e0f31d75eebed584be/engine/core/kernel/include/nau/math/dag_bounds3.h#L209)

**Заключение**

Мы рассмотрели основные методы, которые помогут разработчикам улучшить производительность движка Nau Engine\. Понимание и устранение этих проблем — важный шаг на пути к созданию успешных и увлекательных игр\. В следующей статье мы поговорим о распространённых ошибках при написании классов\.

Спасибо за внимание\!