Опубликован: 24.11.2024 | Доступ: свободный | Студентов: 5 / 0 | Длительность: 02:06:00
Лекция 7:

Лабораторная работа

< Лекция 6 || Лекция 7: 123

Оптимизация

Рассмотрите листинги ассемблерного кода, полученные Clang при разных уровнях оптимизации, выполнив следующие команды:

  • без оптимизаций
    clang -target riscv64-unknown-linux-gnu --sysroot="$GCC_ROOT/sysroot" --gcc-toolchain="$GCC_ROOT" -S -o main.s main.c
    
  • с флагом -O1
    clang -target riscv64-unknown-linux-gnu --sysroot="$GCC_ROOT/sysroot" --gcc-toolchain="$GCC_ROOT" -S -o main-O1.s main.c -O1
    
  • с флагом -O2
    clang -target riscv64-unknown-linux-gnu --sysroot="$GCC_ROOT/sysroot" --gcc-toolchain="$GCC_ROOT" -S -o main-O2.s main.c -O2
    

Будет получен следующий ассемблерный код для функции dot_product:

Компиляция без оптимизаций

dot_product:                            # @dot_product
# %bb.0:
    addi    sp, sp, -64
    sd  ra, 56(sp)                      # 8-byte Folded Spill
    sd  s0, 48(sp)                      # 8-byte Folded Spill
    addi    s0, sp, 64
    sd  a0, -24(s0)
    sd  a1, -32(s0)
    sd  a2, -40(s0)
    li  a0, 0
    sw  a0, -44(s0)
    sd  a0, -56(s0)
    j   .LBB0_1
.LBB0_1:                                # =>This Inner Loop Header: Depth=1
    ld  a0, -56(s0)
    ld  a1, -40(s0)
    bgeu    a0, a1, .LBB0_4
    j   .LBB0_2
.LBB0_2:                                #   in Loop: Header=BB0_1 Depth=1
    ld  a0, -24(s0)
    ld  a1, -56(s0)
    slli    a1, a1, 2
    add a0, a0, a1
    flw ft0, 0(a0)
    ld  a0, -32(s0)
    add a0, a0, a1
    flw ft1, 0(a0)
    flw ft2, -44(s0)
    fmadd.s ft0, ft0, ft1, ft2
    fsw ft0, -44(s0)
    j   .LBB0_3
.LBB0_3:                                #   in Loop: Header=BB0_1 Depth=1
    ld  a0, -56(s0)
    addi    a0, a0, 1
    sd  a0, -56(s0)
    j   .LBB0_1
.LBB0_4:
    flw fa0, -44(s0)
    ld  ra, 56(sp)                      # 8-byte Folded Reload
    ld  s0, 48(sp)                      # 8-byte Folded Reload
    addi    sp, sp, 64
    ret

В строках 3-12 происходит формирование кадра стека функции dot_product: выделяется необходимое для аргументов и локальных переменных место на стеке (строка 3), на стеке сохраняется адрес возврата и адрес предыдущего кадра (строки 4-5), в регистр сохраняется адрес текущего кадра (строка 6), переданные аргументы a, b и n загружаются на стек (строки 7-9), локальные переменные r и i инициализируются нулями (строки 10-12).

В строках 15-17 вычисляется, нужно ли выполнять очередную итерацию цикла: со стека в регистры загружаются значения переменных i и n (строки 15-16), а затем сравниваются (строка 17).

В строках 20-30 происходит вычисление очередной итерации цикла: со стека в регистр загружается значение переменной i (строка 21), вычисляются адреса в памяти значений a[i] и b[i] и они загружаются в регистры (строки 20, 22-27), со стека в регистр загружается значение переменной r (строка 28), к значению r прибавляется результат a[i] * b[i] (строка 29), новое значение r записывается на стек (строка 30).

В строках 33-35 происходит увеличение счётчика цикла i после выполнения очередной итерации: со стека в регистр загружается значение переменной i (строка 33), значение переменной i увеличивается на 1 (строка 34), новое значение переменной i записывается на стек (строка 35).

В строках 38-42 происходит возврат результата после выполнения цикла: со стека в регистр, через который возвращается результат, загружается значение переменной r (строка 38), со стека в регистры загружаются адрес возврата и адрес предыдущего кадра стека (строки 39-40), очищается кадр стека (строка 41), происходит возврат из функции dot_product (строка 42).

Компиляция с флагом -O1

dot_product:                            # @dot_product
# %bb.0:
    fmv.w.x fa0, zero
    beqz    a2, .LBB0_2
.LBB0_1:                                # =>This Inner Loop Header: Depth=1
    flw ft0, 0(a0)
    flw ft1, 0(a1)
    fmadd.s fa0, ft0, ft1, fa0
    addi    a2, a2, -1
    addi    a1, a1, 4
    addi    a0, a0, 4
    bnez    a2, .LBB0_1
.LBB0_2:
    ret

Полученный ассемблерный код значительно короче, полученного без применения оптимизаций. Это получается за счёт того, что на стеке не выделяется место под аргументы и локальные переменные, и все вычисления производятся с регистрами без обращений к памяти.

В строке 3 происходит инициализация регистра, в котором хранится значение r, нулём.

В строке 4 происходит сравнение регистра, в котором хранится значение n, с нулём, чтобы начать выполнение цикла.

В строках 6-7 происходит загрузка в регистры значений a[0] и b[0].

В строке 8 к значению r прибавляется результат a[0] * b[0].

В строке 9 значение n уменьшается на 1.

В строках 10-11 увеличиваются значения регистров, в которых хранятся адреса массивов a и b, чтобы на следующей итерации a[0] и b[0] соответствовали следующим элементам массивов.

В строке 12 происходит сравнение регистра, в котором хранится значение n, с нулём, чтобы узнать, нужно ли выполнять очередную итерацию цикла.

В строке 14 происходит возврат из функции dot_product.

Таким образом, данный код работает аналогично неоптимизированному, однако выполняет гораздо меньше "дорогих" обращений к памяти.

Компиляция с флагом -O2

dot_product:                            # @dot_product
# %bb.0:
    beqz    a2, .LBB0_4
# %bb.1:
    li  a3, 8
    andi    a6, a2, 7
    bgeu    a2, a3, .LBB0_5
# %bb.2:
    fmv.w.x fa0, zero
    li  a2, 0
    bnez    a6, .LBB0_8
.LBB0_3:
    ret
.LBB0_4:
    fmv.w.x fa0, zero
    ret
.LBB0_5:
    andi    a2, a2, -8
    fmv.w.x fa0, zero
    li  a4, 0
    neg a2, a2
    addi    a5, a1, 16
    addi    a3, a0, 16
.LBB0_6:                                # =>This Inner Loop Header: Depth=1
    flw ft0, -16(a3)
    addi    a4, a4, -8
    flw ft1, -16(a5)
    flw ft2, -12(a5)
    fmadd.s ft0, ft0, ft1, fa0
    flw ft1, -12(a3)
    fmadd.s ft0, ft1, ft2, ft0
    flw ft1, -8(a3)
    flw ft2, -8(a5)
    fmadd.s ft0, ft1, ft2, ft0
    flw ft1, -4(a3)
    flw ft2, -4(a5)
    fmadd.s ft0, ft1, ft2, ft0
    flw ft1, 0(a3)
    flw ft2, 0(a5)
    fmadd.s ft0, ft1, ft2, ft0
    flw ft1, 4(a3)
    flw ft2, 4(a5)
    fmadd.s ft0, ft1, ft2, ft0
    flw ft1, 8(a3)
    flw ft2, 8(a5)
    fmadd.s ft0, ft1, ft2, ft0
    flw ft1, 12(a3)
    flw ft2, 12(a5)
    addi    a5, a5, 32
    addi    a3, a3, 32
    fmadd.s fa0, ft1, ft2, ft0
    bne a2, a4, .LBB0_6
# %bb.7:
    neg a2, a4
    beqz    a6, .LBB0_3
.LBB0_8:
    slli    a2, a2, 2
    add a3, a0, a2
    flw ft0, 0(a3)
    add a3, a1, a2
    flw ft1, 0(a3)
    li  a3, 1
    fmadd.s fa0, ft0, ft1, fa0
    beq a6, a3, .LBB0_3
# %bb.9:
    addi    a3, a2, 4
    add a4, a0, a3
    add a3, a3, a1
    flw ft1, 0(a3)
    li  a3, 2
    flw ft0, 0(a4)
    fmadd.s fa0, ft0, ft1, fa0
    beq a6, a3, .LBB0_3
# %bb.10:
    addi    a3, a2, 8
    add a4, a0, a3
    add a3, a3, a1
    flw ft1, 0(a3)
    li  a3, 3
    flw ft0, 0(a4)
    fmadd.s fa0, ft0, ft1, fa0
    beq a6, a3, .LBB0_3
# %bb.11:
    addi    a3, a2, 12
    add a4, a0, a3
    add a3, a3, a1
    flw ft1, 0(a3)
    li  a3, 4
    flw ft0, 0(a4)
    fmadd.s fa0, ft0, ft1, fa0
    beq a6, a3, .LBB0_3
# %bb.12:
    addi    a3, a2, 16
    add a4, a0, a3
    add a3, a3, a1
    flw ft1, 0(a3)
    li  a3, 5
    flw ft0, 0(a4)
    fmadd.s fa0, ft0, ft1, fa0
    beq a6, a3, .LBB0_3
# %bb.13:
    addi    a3, a2, 20
    add a4, a0, a3
    add a3, a3, a1
    flw ft1, 0(a3)
    li  a3, 6
    flw ft0, 0(a4)
    fmadd.s fa0, ft0, ft1, fa0
    beq a6, a3, .LBB0_3
# %bb.14:
    addi    a2, a2, 24
    add a0, a0, a2
    flw ft0, 0(a0)
    add a0, a1, a2
    flw ft1, 0(a0)
    fmadd.s fa0, ft0, ft1, fa0
    ret
< Лекция 6 || Лекция 7: 123