Оптимизация указателя постоянной функции

Я пытаюсь реализовать абстрактный интерфейс в C, используя указатели на функции внутри структуры.
Что-то вроде следующего

typedef int (*fn_t)(int);
typedef struct
{
    int x;
    const fn_t fnp;
}struct_t;

__attribute__((optimize("O0"))) int square(int num) 
{
    return num * num;
}

static struct_t test = {.fnp = square};

int main(void)
{
    test.x = 1;

    int fnp_ret = test.fnp(3);

    return (fnp_ret);
}

При сборке в godbolt с -O3 с использованием ARM-GCC-13.2.0 неизвестного-eabi результат будет следующим.

square:
        str     fp, [sp, #-4]!
        add     fp, sp, #0
        sub     sp, sp, #12
        str     r0, [fp, #-8]
        ldr     r3, [fp, #-8]
        mov     r2, r3
        mul     r2, r3, r2
        mov     r3, r2
        mov     r0, r3
        add     sp, fp, #0
        ldr     fp, [sp], #4
        bx      lr
main:
        mov     r1, #1
        ldr     r3, .L5
        mov     r0, #3
        ldr     r2, [r3, #4]
        str     r1, [r3]
        bx      r2
.L5:
        .word   .LANCHOR0

Здесь можно видеть, что в main() созданная сборка сначала находит указатель функции в структуре, а затем разыменовывает его. Я нахожу это странным, поскольку указатель на функцию — это const, поэтому я ожидал, что компилятор поймет, что он всегда указывает на функцию square, поэтому это будет эквивалентно прямому вызову функции square. Видимо, здесь дело не в этом.

Во время эксперимента я заметил, что в случае, если оператор test.x = 1; закомментирован, сборка делает то, что я ожидал, вызывая функцию square напрямую.

square:
        str     fp, [sp, #-4]!
        add     fp, sp, #0
        sub     sp, sp, #12
        str     r0, [fp, #-8]
        ldr     r3, [fp, #-8]
        mov     r2, r3
        mul     r2, r3, r2
        mov     r3, r2
        mov     r0, r3
        add     sp, fp, #0
        ldr     fp, [sp], #4
        bx      lr
main:
        mov     r0, #3
        b       square

Чего мне не хватает?
Есть ли способ надежно реализовать это без потери производительности, описанной выше?

Вы заботитесь о производительности, но функциональность определенно не оптимальна

gulpr 28.03.2024 21:27

@gulpr: Эта функция является заполнителем для демонстрации. Его оптимизация или ее отсутствие не имеют значения для демонстрации.

Eric Postpischil 28.03.2024 21:30

@EricPostpischil Думаю, ОП хотел noinline

gulpr 28.03.2024 21:32

Это может быть специфично для компилятора/оптимизатора Arm. Когда я создаю ваш первый пример для x86, под gcc (8.3.1) main есть три строки: movl $1, test(%rip); movl $3, %edi; jmp *test+8(%rip); Это то, что делает ваш второй тест (но не «встраивает» указатель функции). Но в clang (7.0.1) это: movl $9, %eax; retq; Итак, это может быть «преждевременная оптимизация». (т. е. не волнуйтесь — будьте счастливы!). Вы сохраняете только одну ldr инструкцию. Что может быть более интересным, так это зациклить вызов и посмотреть, кэшируется ли адрес в регистре.

Craig Estey 28.03.2024 21:44
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
96
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
  1. оптимизировать O0 — неправильный вариант. Вы хотите noinline
  2. Это хорошо известный недостаток оптимизатора gcc. Если вы коснетесь любого члена структуры, вся структура будет считаться недействительной const
__attribute__((noinline)) int square(int num) 
{
    return num * num;
}

Что мне не хватает? Есть ли способ надежно реализовать это без платить за снижение производительности, описанное выше?

Боюсь, вы ничего не сможете с этим поделать. Скорее всего, оно никогда не будет отсортировано. если для вас это важно, вы можете использовать clang: https://godbolt.org/z/T4bznYE4h

Спасибо за ответ. Действительно, я хотел избежать встраивания в этот атрибут, так что спасибо и за это. Что касается потока оптимизатора GCC, не могли бы вы дать мне несколько советов для его дальнейшего исследования? Вероятно, какая-то проблематическая ссылка или ключевые слова. еще раз спасибо

user3387106 28.03.2024 23:43

@user3387106: user3387106: В зависимости от того, что вы хотите предотвратить, вам может потребоваться __attribute__((noinline,noipa)) полностью предотвратить любой межпроцедурный анализ (например, функция является чистой, поэтому вызов все равно можно пропустить, если возвращаемое значение не используется, или создать собственное соглашение о вызовах, например, вызывающая сторона пользуется тем фактом, что функция не затирает регистр, который в ABI обычно затирается вызовом.) Атрибут noclone может быть важен для того, чтобы не позволить GCC изобретать версию функции с одним или больше аргументов распространяется константно, но noipa покрывает это.

Peter Cordes 28.03.2024 23:49

Другие вопросы по теме