#c #g #clang
#c #g #clang
Вопрос:
Я столкнулся с очень необычной проблемой при использовании clang-1200.0.32.21 в Mac OS Catalina 10.15.7.
По сути, компиляция этого кода занимает много времени и имеет чрезвычайно высокое использование оперативной памяти (около 10 ГБ ОЗУ):
m_Table = new MapGenerator::Block[MAP_DIAMETER * MAP_HEIGHT * MAP_DIAMETER]();
===-------------------------------------------------------------------------===
Clang front-end time report
===-------------------------------------------------------------------------===
Total Execution Time: 50.8973 seconds (55.3352 wall clock)
---User Time--- --System Time-- --User System-- ---Wall Time--- --- Name ---
34.9917 (100.0%) 15.9056 (100.0%) 50.8973 (100.0%) 55.3352 (100.0%) Clang front-end timer
34.9917 (100.0%) 15.9056 (100.0%) 50.8973 (100.0%) 55.3352 (100.0%) Total
Изменение его на следующее мгновенно исправляет это:
uint32_t table_size = Map::MAP_DIAMETER * Map::MAP_HEIGHT * Map::MAP_DIAMETER * sizeof(MapGenerator::Block);
m_Table = reinterpret_cast<MapGenerator::Block*>(malloc(table_size));
memset(m_Table, 0, table_size);
===-------------------------------------------------------------------------===
Clang front-end time report
===-------------------------------------------------------------------------===
Total Execution Time: 1.3105 seconds (2.1116 wall clock)
---User Time--- --System Time-- --User System-- ---Wall Time--- --- Name ---
1.1608 (100.0%) 0.1497 (100.0%) 1.3105 (100.0%) 2.1116 (100.0%) Clang front-end timer
1.1608 (100.0%) 0.1497 (100.0%) 1.3105 (100.0%) 2.1116 (100.0%) Total
Если вам интересно, вот соответствующие определения:
enum : int { MAP_HEIGHT = 15 };
enum : int { MAP_DIAMETER = 1000 };
union Block
{
struct
{
uint8_t valid : 1; // block valid
/* used to determine visible faces */
uint8_t v_top : 1;
uint8_t v_bottom : 1;
uint8_t v_front : 1;
uint8_t v_back : 1;
uint8_t v_right : 1;
uint8_t v_left : 1;
uint8_t v_base : 1;
uint8_t discard : 1; // delete flag
};
uint16_t bits;
};
Block* m_Table;
Есть ли какая-либо логическая причина, по которой компиляция new занимает так много времени? Насколько я понимаю, это не должно отличаться. Кроме того, у меня нет этой проблемы в компиляторе MSVC (Microsoft C ) в Windows.
Редактировать:
Это минимально воспроизводимый образец:
#include <cstdint>
#include <cstdlib>
#include <cstring>
enum : int { MAP_HEIGHT = 15 };
enum : int { MAP_DIAMETER = 1000 };
union Block
{
struct
{
uint8_t valid : 1; // block valid
/* used to determine visible faces */
uint8_t v_top : 1;
uint8_t v_bottom : 1;
uint8_t v_front : 1;
uint8_t v_back : 1;
uint8_t v_right : 1;
uint8_t v_left : 1;
uint8_t v_base : 1;
uint8_t discard : 1; // delete flag
};
uint16_t bits;
};
Block* table = nullptr;
// UNCOMMENT THIS:
// #define USE_MALLOC
int main(int argc, char* argv[])
{
#ifndef USE_MALLOC
table = new Block[MAP_DIAMETER * MAP_HEIGHT * MAP_DIAMETER]();
#else
uint32_t table_size = MAP_DIAMETER * MAP_HEIGHT * MAP_DIAMETER * sizeof(Block);
table = reinterpret_cast<Block*>(malloc(table_size));
memset(table, 0, table_size);
#endif
(void) table;
#ifndef USE_MALLOC
delete[] table;
#else
free(table);
#endif
return 0;
}
Я компилирую его с помощью команды:
g -std=c 17 -g -Wall -Werror -ftime-report -c test.cpp -o test.o
Комментарии:
1. Это абсолютно разные случаи. Оператор new создает каждый элемент в массиве (применяя неявный конструктор), malloc этого не делает. Определите явный конструктор для его наблюдения.
2. @S.M. Я бы ожидал, что это будет связано с производительностью во время выполнения, хотя это проблема времени компиляции. У GCC нет этой проблемы, как и у MSVC, только clang делает это.
3. Clang может оптимизировать (предварительно подсчитать) этот массив во время компиляции и просто поместить его как memcpy во время выполнения.
4. Я забыл упомянуть, что clang также занимает 10 ГБ оперативной памяти с новым подходом operator. Я добавил эту деталь.