Как глобализация 2 небольших массивов может привести к значительному снижению производительности?


У меня есть 2 небольших локальных массива:

short int xpLeft [4], xpRight [4];

В тот момент, когда я делаю их глобальными (для доступа к ним в другом методе, но только в одном файле C (например, недоступном для других модулей)), производительность (на Motorola 68000) падает. Вместо 224 вбланков (для локальных), весь бенчмарк (рендерит 320 кадров сцены) вдруг занимает 249 вбланков (глобальный массив)!

Что я пробовал:
Поскольку данные в массивах не использовались в этой функции, я решил, что компилятор поймал, что и не удосужился записать полученное значение (из регистра) в память (крайне медленная операция на 68000 - обращение к памяти). Поэтому я добавил небольшой код для использования этих значений массива в конце функции, и это соответственно повысило стоимость производительности (всего 1 vblank).

Что могло бы помочь:
Мне нужно изучить окончательный код ASM (и сравнить обе версии), но я не знаю, как это сделать с помощью компилятора vbcc (от Dr.Volker). Я попробовал несколько переключатели из документации, и хотя они производили некоторый промежуточный вывод, я не мог заставить его предоставить полный список ASM каждого модуля (с именами функций из C).

Я только что включил переключатель "- к". Очевидно, порядок переключений имеет значение, и я нашел место в командной строке, где он распознается, и я, наконец, получил *.Вывод ASM (с более чем 300 тысячами строк не меньше), но у меня наконец-то есть что-то (ASM с символами), чтобы копаться.

Что Я думаю, что происходит:

  1. создание массивов глобальными помещает их в разные адреса в оперативной памяти, и контроллер памяти должен получить доступ к другому банку, и банк переключатель-это чрезвычайно медленная операция на целевой платформе. - приводя к циклам заряда RAS (для доступа к различным рядам адреса).
  2. сглаживание указателя-возможно, компилятор генерирует другой код и может фактически обращаться к фактической памяти для промежуточного результаты - но если бы у меня был выход ASM для каждая функция, я мог бы понять это легко

Какие-либо советы о том, почему это происходит или как получить полный список выходных данных vbcc каждого скомпилированного модуля с соответствующим кодом ASM ?

С помощью вывода ASM я создал небольшой тестовый репро-кейс:

short int tmpfn1 ()
{
    short int xpLeft [4], xpRight [4];
    short int i, tmp;

    for (i = 0; i < 4; i++)
    {
        xpLeft [i] = 137 + i;
        xpRight [i] = 215 + i;
    }

    tmp = xpLeft [0] + xpRight [0];
    return tmp;
}

Вот результат ASM. Хотя ASM довольно понятен, я все равно добавил несколько комментариев:

    public  _tmpfn1
    cnop    0,4
_tmpfn1
    sub.w   #16,a7
    movem.l l4150,-(a7)
    moveq   #0,d1
    lea (0+l4152,a7),a1   ; a1 = &xpLeft [0]
    lea (8+l4152,a7),a2   ; a2 = &xpRight [0]
    move.w  #215,d3    ; d2/d3 = The Bulgarian constants 
    move.w  #137,d2
l4148
    move.w  d1,d0
    ext.l   d0
    lsl.l   #1,d0
    move.w  d2,(0,a1,d0.l)    ; xpLeft [i] = 137 + i;
    move.w  d3,(0,a2,d0.l)    ; xpRight [i] = 215 + i;
    addq.w  #1,d1    ; d1 = Loop Counter (i++)
    addq.w  #1,d2
    addq.w  #1,d3
    cmp.w   #4,d1
    blt l4148    ; Repeat the loop
    move.w  (8+l4152,a7),d0
    add.w   (0+l4152,a7),d0    ; tmp = xpLeft [0] + xpRight [0];
l4150   reg a2/d2/d3
    movem.l (a7)+,a2/d2/d3
    add.w   #16,a7
l4152   equ 12
    rts
; stacksize=28
    opt 0
    opt NQLPSMRBT

Теперь я перейду к размещению массивов из локального в глобальный.

Вот код с глобальная переменная.

    public  _tmpfn1
    cnop    0,4
_tmpfn1
    movem.l l4150,-(a7)
    moveq   #0,d1
    move.w  #215,d2
    move.w  #137,d3
l4148
    move.w  d1,d0
    ext.l   d0
    lsl.l   #1,d0
    lea _AxpLeft,a0
    move.w  d3,(0,a0,d0.l)
    lea _AxpRight,a0
    move.w  d2,(0,a0,d0.l)
    addq.w  #1,d1
    addq.w  #1,d3
    addq.w  #1,d2
    cmp.w   #4,d1
    blt l4148
    move.w  _AxpRight,d0
    add.w   _AxpLeft,d0
l4150   reg d2/d3
    movem.l (a7)+,d2/d3
l4152   equ 8
    rts
; stacksize=8
    opt 0
    opt NQLPSMRBT
Единственное различие - это две инструкции lea, которые, если память не изменяет, составляют максимум 16 циклов.
Должно быть, что-то еще происходит с самой функцией, но по какой-то причине ее код запутан в ASM (в ASM всего 6 строк, никаких переходов, никаких других меток, ничего). Я продолжу искать ASM, где именно находится код.
1 2

1 ответ:

Как я и подозревал, есть момент, связанный с компилятором monster-WTF. Причина, по которой в случае локальных переменных было всего 6 строк кода, заключается в том, что компилятор смог вычислить, что эти 120 строк кода C на самом деле ничего не делают на глобальном уровне, поэтому он полностью игнорировал код ! То есть код ASM для метода был всего лишь этими 6 строками (с rts). Это, однако, не имеет большого смысла с результатами бенчмарка, которые я получил (но это будет по-другому рассказ)

Мораль этой истории: сделав переменную глобальной, компилятор фактически потрудился создать код для функции (а не просто пустой 6-ОП заглушку). И поскольку я встроил все внутри этой функции, не было никаких вложенных вызовов функций. Это, очевидно, звучит смешно, учитывая, что у меня было около 25 сеансов отладки с переменными и выводом на целевом устройстве. Но в тот момент, когда я удалил эти внешние вызовы печати / отладки, это, должно быть, был момент компилятора вообще не создавал функциональный код. А-кровавый мазинг....