Clang C новый оператор большой динамический массив медленное время компиляции

#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. Я добавил эту деталь.