Тема: C\C++
Показать сообщение отдельно
Старый 28.02.2015, 02:37   #7
Samodelkin
Мастер
 
Регистрация: 12.01.2009
Сообщений: 983
Написано 390 полезных сообщений
(для 634 пользователей)
Ответ: C\C++

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

Кстати в твоём примере показана интересная особенность языка С -- из за отсутствия inline такие короткие функции могут стать причиной частых кеш-промахов и большого оверхеда на сам вызов функций. У себя в рейкастинге я в критических местах не пользовался даже готовыми матфункциями, а писал сразу хард-кодом целый блок вычислений. В целом в услвиях достаточно стабильного графического пайплайна рейкастинга не требуется частых переписываний, поэтому выбранный мной подход пока себя оправдывает.

Причина почему в d3dx использовали такой подход я думаю кроется в том числе в намерении сделать библиотеки совместимые со множеством разных нативных языков. Не думаю что думать о таких вещах правильно когда цель gapi вычислять графику как можно быстрее. В dx11 они вроде сосредоточились исключительно на с++11 (из нативных языков, а так конечно вездесущий шарп там есть тоже).

Вообще если вернуться к вопросу оптимизации то твоя структура не очень правильно написана:
typedef struct entity_s {
    vec3_t localPosition;
    vec3_t localScale;
    quat_t localRotation;
    mat4_t globalTransform;
    mat4_t localTransform;
    mat4_t invBindTransform;
    struct entity_s * parent;
    list_t surfaces;
    list_t childs;
    char skinned;
    char name[64];
    char propBuffer[512];
    list_t keyFrames;
    int totalFrames;
    anim_t * anim;
    char visible;
    char animated;
    char globalTransformCalculated;
    float depthHack;
    /* components */
    camera_t * camera;
    light_t * light;
    body_t * body;
    struct entity_s *instanceOf;
} entity_t;
Например такие толстые вещи как name[ 64 ] и propBuffer[ 512 ] нужно убирать вниз, потому что следом за ними идут более тонкие и нужные поля, а префетчер начинает тратить время на чтение этих толстых массивов и не подготавливает вовремя нужные данные в кеш.
Ещё лучше делить такие большие структуры на "горячие" и "холодные" части. Предположим что первые три поля используются очень часто (каждый кадр или чаще), а другие реже, по мере трансформации объекта или запроса на изменение (ну тут ты сам лучше знаешь что и когда у тебя используется). Поэтому данные лучше распределить по двум структурам.
// Первая:
typedef struct {
    vec3_t localPosition;
    vec3_t localScale;
    quat_t localRotation;
} entityHot_t;

// Вторая:
typedef struct {
    // Всё остальное.
} entityCold_t;
Теперь множество этих структур будет расположено двумя массивами так:
entityHot_t hotEntities[ number ];
entityCold_t coldEntities[ number ];
Такое размещение называется Structure Of Arrays (SOA). В случае большой структуры как у тебя это Array Of Structs (AOS).
Обход первого массива (hotEntities) займет гораздо меньше времени, потому что потребуется прокешировать гораздо меньше данных чем во втором случае.

Возможно в языках с таким синтаксисом это не очень удобно, или же потребуется что-то придумать с макросами или обёртками. Но я видел более "заточенные" для игр языки где это решалось вот так:
struct entity_t {
    vec3_t SOA pos; // hot part
    char SOA name[ 512 ];
};

// Теперь когда делаем массив ентити:
entity_t entities[ 1024 ];
Компилятор видит метки SOA и размещает данные в памяти так что сначала идёт массив из 1024 векторов, а затем массив из массивов чаров. Таким образом горячая часть остается компактной на уровне железа, а программист видит абстракцию и продолжает работать со структурой как единым целым.
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
St_AnGer (28.02.2015)