LINUX.ORG.RU

Медленная компиляция большого массива в Crystal (LLVM).

 ,


0

1

Есть следующий кусок кода на Crystal

GMULT_AES = [0x00, 0x00, 0x00, 0x00, ...]
puts GMULT_AES[12000]

Элементов в массиве 65535.
При компиляции с флагом --release сильно возрастает потребление памяти и время компиляции.
При компиляции был получен такой кусок LLVM IR

define internal void @"~GMULT_AES:init"() {
alloca:
  %__temp_386 = alloca i32*
  %capacity = alloca i32
  %ary = alloca %"Array(Int32)"*
  br label %entry

entry:                                            ; preds = %alloca
  store i32 65536, i32* %capacity
  %0 = load i32, i32* %capacity
  %1 = call %"Array(Int32)"* @"*Array(Int32)@Array(T)::new<Int32>:Array(Int32)"(i32 596, i32 %0)
  store %"Array(Int32)"* %1, %"Array(Int32)"** %ary
  %2 = load %"Array(Int32)"*, %"Array(Int32)"** %ary
  %3 = load %"Array(Int32)"*, %"Array(Int32)"** %ary
  %4 = getelementptr inbounds %"Array(Int32)", %"Array(Int32)"* %3, i32 0, i32 3
  %5 = load i32*, i32** %4
  store i32* %5, i32** %__temp_386
  %6 = load i32*, i32** %__temp_386
  %7 = call i32 @"*Pointer(Int32)@Pointer(T)#[]=<Int32, Int32>:Int32"(i32* %6, i32 0, i32 0)
  %8 = load i32*, i32** %__temp_386
  %9 = call i32 @"*Pointer(Int32)@Pointer(T)#[]=<Int32, Int32>:Int32"(i32* %8, i32 1, i32 0)
  %10 = load i32*, i32** %__temp_386
  %11 = call i32 @"*Pointer(Int32)@Pointer(T)#[]=<Int32, Int32>:Int32"(i32* %10, i32 2, i32 0)
  %12 = load i32*, i32** %__temp_386
  %13 = call i32 @"*Pointer(Int32)@Pointer(T)#[]=<Int32, Int32>:Int32"(i32* %12, i32 3, i32 0)
  %14 = load i32*, i32** %__temp_386

Аллокаций тут вроде не так много, но зато много строк с load/call, которые вызываются на каждый элемент массива
Из-за чего так происходит?
Как более правильно должен создаваться массив с большим кол-ом элементов, чтобы это не сказывалось на скорости компиляции?
P.S. Аналогичную проблему нашёл и в баг-трекере Rust-а https://github.com/rust-lang/rust/issues/49330


Странно что llvm не упростил инициализацию массива. В примере с растом дургой случай, там ничего не сделаешь - придётся ждать. В данном примере если массив static, то не нужно явно задавать элементы, он и так будет нулевым. Если он локальный, то нужно сделать memset.

alexanius ★★ ()
Ответ на: комментарий от alexanius

Исходник - https://gist.github.com/MrSorcus/f6a19bf52f499743f54448161430b807
Компилирую так crystal build --release gmult_aes.cr
Как результат - на моём железе уходит 5 минут на компиляцию и ~3GB оперативы.
При этом без флага --release компилирует за 10 секунд и несколько сотен MB.

Sorcus ()
Ответ на: комментарий от Sorcus

В общем, чтобы разобраться что конкретно случилось надо лезть в исходники, но по внешним признакам у меня сложилось следующее ощущение.

Проблема не в llvm, т.к. сборка аналогичного файла через clang с оптимизациями проходит быстро. Проблема возникает только с опцией --release, которая включает оптимизации. Скорей всего у них какая-то из оптимизаций начинает анализировать содержимое массива, строить модель памяти и анализировать её. Для массивов такого размера это очень долго и затратно. Причём оптимизация эта очень может быть что даже до трансляции в llvm происходит.

В общем, рекомендую оформить ошибку на разработчиков crystal с описанием ситуации.

alexanius ★★ ()