Unreal Engine C++ Course
PatreonBoostyTelegramYoutube
  • О курсе
  • Ссылки
    • Git
    • Visual Studio
    • Unreal Engine
  • Глоссарий
    • Общие понятия
    • Код / C++
      • Const correctness
      • Использование auto
    • Код / Best practices
    • Анимации
    • UI
    • VFX
    • AI
  • Unreal Editor Hotkeys
  • Visual Studio Hotkeys
  • Console commands
  • Проблемы и решения
    • IntelliSense тормозит
    • Проект не компилируется
    • Не вижу репозиторий ShootThemUp
    • Класс не отображается в Solution Explorer
    • Не работает Apply Damage
    • Где взять текстуры интерфейса
    • Нет контекстного меню UE при клике правой кнопкой мыши на файле .uproject
    • Ошибка Unable to start program
  • Вопросы и ответы
    • Зачем вступать в организацию EpicGames на Github
    • Можно ли проходить курс на UE5
    • Как перегенерировать файлы проекта
    • Насколько сильно влияет кол-во инклюдов на работу кода
    • На сколько большие могут быть структуры и таблицы
    • Медленный ли Cast
    • Что такое интерфейсы
    • Как работает код с переопределением GetMaxSpeed
    • Hit Reaction Animation
  • Дополнения к урокам
    • Лекция 065
      • Additive animation при приземлении
      • Блокировка стрельбы при беге
    • Лекция 079
      • Пикап видим после начисления патронов
      • Пикап не подбирается, если боекомплект расходуется в точке респауна пикапа
    • Лекция 089
      • NiagaraSystem не аттачится к дулу
    • Лекция 148
    • Лекция 155
  • Как задавать вопрос
  • Что почитать
  • UE5
  • Automation
    • Форматирование кода
  • VPN
  • 👨‍🎓мои курсы
    • 🧪Автоматизация и тестирование в Unreal Engine
    • 🐍Snake game
  • 🔗lifeexe сообщество
    • Поддержать проект
    • Ресурсы
    • Проекты участников сообщества
    • Code review участников сообщества
Powered by GitBook
On this page

Was this helpful?

  1. Вопросы и ответы

На сколько большие могут быть структуры и таблицы

PreviousНасколько сильно влияет кол-во инклюдов на работу кодаNextМедленный ли Cast

Last updated 4 years ago

Was this helpful?

На сколько большие могут быть структуры? Я имею в виду есть ли какой то лимит по параметрам у структур за который не следует переходить? Например есть структура в которой 100 параметров из них половина это другие структуры и т. д.

Сложно абстрактно ответить на такой вопрос, поэтому опишу все моменты, которые важны. Получилась мини лекция )

1. Первое, самое простое, о чем стоит задуматься, когда видим в коде гигантскую структуру — все ли у нас верно с точки зрения семантики, т.е. имеет ли смысл такой сложный тип данных или это оверхэд. Может создать несколько простых типов; может стоит использовать базу данных (в случае с базой данных могут вступить в игру другие детали: как часто мы читаем данные, нужно ли поля отображать в блюпринтах и т.д.). Также я всегда рекомендую при написании кода думать о том, будет ли он понятен другому человеку при прочтении, это помогает.

2.1 Размер структуры влияет на физическую память. Размер структуры в байтах равен количеству байт занимаемых каждым полем. Сколько байтов соответствует каждому типу в Unreal можно посмотреть в . Указатели имеют размер 8 байт на 64х-битной машине вне зависимости от типа.

Пример:

USTRUCT(BlueprintType)
struct FSimpleStructInt32
{
    GENERATED_USTRUCT_BODY()
 
    int32 Data;
};
 
USTRUCT(BlueprintType)
struct FSimpleStructDouble
{
    GENERATED_USTRUCT_BODY()
 
    double Data;
};
 
USTRUCT(BlueprintType)
struct FSimpleStructDoublePtr
{
    GENERATED_USTRUCT_BODY()
 
    double* Data;
};
 
USTRUCT(BlueprintType)
struct FSimpleStructTexPtr
{
    GENERATED_USTRUCT_BODY()
 
    UTexture2D* Data;
};
 
UE_LOG(LogTemp, Display, TEXT("int32 size: %i  FSimpleStructInt32 size: %i "), sizeof(int32), sizeof(FSimpleStructInt32));
UE_LOG(LogTemp, Display, TEXT("double size: %i  FSimpleStructDouble size: %i "), sizeof(double), sizeof(FSimpleStructDouble));
UE_LOG(LogTemp, Display, TEXT("double ptr size: %i  FSimpleStructDoublePtr size: %i "), sizeof(double*), sizeof(FSimpleStructDoublePtr));
UE_LOG(LogTemp, Display, TEXT("tex size: %i, tex ptr size: %i  FSimpleStructTexPtr size: %i "), sizeof(UTexture2D), sizeof(UTexture2D*), sizeof(FSimpleStructTexPtr));

Вывод получится следующий:

LogTemp: Display: int32 size: 4  FSimpleStructInt32 size: 4 
LogTemp: Display: double size: 8  FSimpleStructDouble size: 8 
LogTemp: Display: double ptr size: 8  FSimpleStructDoublePtr size: 8 
LogTemp: Display: tex size: 848, tex ptr size: 8  FSimpleStructTexPtr size: 8

Eсли у вас в структуре 1000 переменных типа int32 , то размер структуры будет 4 * 1000 байт.

Eсли у вас в структуре 1000 переменных типа double , то размер структуры будет 8 * 1000 байт.

Пример:

USTRUCT(BlueprintType)
struct FSimpleAlignTest
{
    GENERATED_USTRUCT_BODY()
 
   int32 Data1;
   int32* Data2;
   uint8 Data3;
   uint8* Data4;
};
 
UE_LOG(LogTemp, Display, TEXT("int32 size: %i"), sizeof(int32));
UE_LOG(LogTemp, Display, TEXT("int32 ptr size: %i"), sizeof(int32*));
UE_LOG(LogTemp, Display, TEXT("uint8 size: %i"), sizeof(uint8));
UE_LOG(LogTemp, Display, TEXT("uint8 ptr size: %i"), sizeof(uint8*));
UE_LOG(LogTemp, Display, TEXT("struct size: %i"), sizeof(FSimpleAlignTest));

Вывод:

LogTemp: Display: int32 size: 4
LogTemp: Display: int32 ptr size: 8
LogTemp: Display: uint8 size: 1
LogTemp: Display: uint8 ptr size: 8
LogTemp: Display: struct size: 32

Т.е. поля Data1 и Data2 выравнялись до 8 байт, поэтому получилось 4 * 8 = 32 байта (хотя сумма 4 + 8 + 1 + 8 = 21 байт)

Далее, меняем порядок полей у той же структуры:

USTRUCT(BlueprintType)
struct FSimpleAlignTest
{
    GENERATED_USTRUCT_BODY()
 
   int32 Data1;
   uint8 Data2;
   int32* Data3;
   uint8* Data4;
};
 
UE_LOG(LogTemp, Display, TEXT("int32 size: %i"), sizeof(int32));
UE_LOG(LogTemp, Display, TEXT("uint8 size: %i"), sizeof(uint8));
UE_LOG(LogTemp, Display, TEXT("int32 ptr size: %i"), sizeof(int32*));
UE_LOG(LogTemp, Display, TEXT("uint8 ptr size: %i"), sizeof(uint8*));
UE_LOG(LogTemp, Display, TEXT("struct size: %i"), sizeof(FSimpleAlignTest));

Вывод:

LogTemp: Display: int32 size: 4
LogTemp: Display: uint8 size: 1
LogTemp: Display: int32 ptr size: 8
LogTemp: Display: uint8 ptr size: 8
LogTemp: Display: struct size: 24

Два первых поля занимают 5 байт, они упаковались вместе в 8 байт, поэтому получилось 8 * 3 = 24 байта (хотя сумма все та же 21 байт)

2.2 Теперь смоделируем ситуацию, которую вы описали. В C++ имееются ограничения на размер объекта, который можно создать на стеке. В примере я использую шаблон TFixedAllocator, который позволяет аллоцировать массив фиксированного размера:

USTRUCT(BlueprintType)
struct FDataStruct1
{
    GENERATED_USTRUCT_BODY()
 
    TArray<int32, TFixedAllocator<1024>> SomeIntData;
};
 
USTRUCT(BlueprintType)
struct FDataStruct2
{
    GENERATED_USTRUCT_BODY()
 
    TArray<FDataStruct1, TFixedAllocator<1024>> SomeDataStruct1;
};
 
constexpr int32 ArrSize = 400;
 
USTRUCT(BlueprintType)
struct FDataStruct3
{
    GENERATED_USTRUCT_BODY()
 
    TArray<FDataStruct2, TFixedAllocator<ArrSize>> SomeDataStruct2;
};
 
.........
 
 
void ASTUBaseCharacter::BeginPlay()
{
    Super::BeginPlay();
 
    FDataStruct3 data;
}

Код выше скомпилируется. Все ОК (если интересно, можете посчитать сколько памяти занимает FDataStruct3) А теперь увеличим параметр ArrSize до 500:

constexpr int32 ArrSize = 500;

Код не скомпилируется, компилятор выдаст сообщение об ошибке:

fatal error C1126: automatic allocation exceeds 2G

2.3 При создании объекта динамически в куче (heap) таких ограничений нет. USTRUCT динамически создать не получится, но можно, обернуть ее в UObject и создать динамически такой объект, ошибки компиляции уже не будет в данном случае:

UCLASS(Blueprintable, BlueprintType)
class SHOOTTHEMUP_API UStructContainerObject : public UObject
{
    GENERATED_BODY()
public:
    FDataStruct3 Data;
};
 
...
 
const auto DataContainer = NewObject<UStructContainerObject>();

3. Если у вас структура с массивом массивов массивов структур и вы волняете поиск по каким-то ключам, и у вас три вложенных цикла, то тут в игру вступает время поиска (можно почитать в интернете про сложность алгоритмов и т.п.). Производительность может упасть легко.

4. Нужно быть аккуратным с копированием больших структур: не делать лишнего, передавать по ссылке везде, где возможно.

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

Ну и аналогичный вопрос по таблицам. Насколько большими могут быть хранилища данных такие как таблицы? Что лучше 10 маленьких таблиц или 1 большая? Меня интересует на сколько правильно там хранить информацию к которой нужен доступ постоянно ну если не постоянно то с определенной частотой обращаться к этим данным и на сколько велики могут быть эти хранилища для беспроблемной работы.

Если посмотреть в исходники на класс UDataTable, то можно увидеть, что в сухом остатке UDataTable это UObject, который хранит структуры в ассоциативном массиве:

/** Map of name of row to row data structure. */
TMap<FName, uint8*>		RowMap;

Т. е. в целом это проецируется на обсуждение первого вопроса. Можете представить, что вы объявляете структуру, в которой одно поле - ассоциативный массив структур другого типа.

Хранить данные в таблицах нормальное решение + удобство в том, что данный контейнер с данными могут использовать разные несвязанные друг с другом классы.

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

Подводя итог:

А какие самые большие структуры вы видели за свою практику? Может у вас есть примеры из практики?

Постоянно встречаются:

1. Конфигурационные структуры (например оружия): параметры цветов, анимаций, связи модификаций, настройки стрельбы, ущерба в различных ситуациях.

2. Деревья прокачки персонажа.

3. В проектах с пользовательской информацией: когда необходимо запросить из базы данные о зарегистрированных пользователях, распарсить, проанализировать, отсортировать всю информацию, от которой может зависеть весь внешний вид игры в принципе (структура данных может быть очень! сложной; для примера можно посмотреть как работают API различных сервисов: google, twitter и сколько данных можно получить разными запросами).

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

2.2 Структуры выравниваются в памяти (). Ее размер может зависеть от порядка полей структуры.

6.

Универсального решения естественно нет. Все зависит от конкретной ситуации и как вы используете структуру. Я предпочитаю начинать с семантической оценки. Так же можете почитать про (это в целом про классы, но смысл примерно тот же)

документации
здесь наглядно с картинками можно прочитать
Полезные указания по использованию структур из документации
антипаттерн Blob