Член, вызывающий встроенную функцию-член через указатель функции, будет ли он встроен?

У меня есть набор чисто арифметических функций, вызовы которых определяются не при компиляции, а во время выполнения. Я намеревался создать массив указателей на все из них и обрабатывать их вызовы через индексы массива (например, если (a> 3) вызвать 3-й).

Эти функции будут часто и многократно вызываться в цикле, поэтому для повышения производительности они должны быть встроены.

Мой вопрос: будет ли такой вызов через встроенные указатели функций-членов в конечном итоге встроенным?

Спасибо!

class foo{
    private:
    int f(int x){return x;}
    int (foo::*pf)(int);
    public:
    foo(){
        pf=&foo::f;
        (*this.*pf)(3); //will this call be inlined?
        f(3);           //this call is surely inlined
    }
};
int main(){
    foo f;
    return 0;
}

person Zhangyi Hu    schedule 05.02.2013    source источник
comment
Если их нельзя определить во время компиляции, как их можно встроить?   -  person Mysticial    schedule 05.02.2013
comment
Я понимаю так: если(a›3){f2(x)}else{f1(x)}. Это как предварительная обработка только заменой.   -  person Zhangyi Hu    schedule 05.02.2013
comment
Если так важно встроить, вы можете рассмотреть возможность использования переключателя.   -  person imreal    schedule 05.02.2013
comment
@Mysticial: В целом вы правы, но я боюсь, что он может запустить свой код через оптимизатор, обнаружить, что он встроен, и вернуться, крича, что это так. Приведенный выше тестовый пример тривиален, и компилятор знает, что pf ссылается на int f(int), поэтому он известен во время компиляции.   -  person David Rodríguez - dribeas    schedule 05.02.2013
comment
g++ -S. Просто говорю'.   -  person Robᵩ    schedule 05.02.2013


Ответы (4)


Во-первых, никогда нет гарантии, что конкретный вызов будет встроен (за исключением использования расширений, специфичных для компилятора), поэтому «наверняка» в вашем коде не является верной ставкой.

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

После всего сказанного более важным вопросом является то, почему вы беспокоитесь о производительности прямо сейчас? Есть ли у вас реальные точки доступа, определенные с помощью профилировщика, которые вы пытаетесь улучшить, или у вас просто есть «чувство», что это будет проблемой?

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

person Nik Bougalis    schedule 05.02.2013
comment
Ненавижу это говорить, но я начинаю уставать от всех, кто слепо кричит о преждевременной оптимизации. (независимо от того, так это на самом деле или нет) - person Mysticial; 05.02.2013
comment
В общем, я тоже. И стараюсь этого не делать. Но в данном случае этот вопрос действительно напрашивается. - person Nik Bougalis; 05.02.2013
comment
Я обычно соглашаюсь со слепыми преждевременными вызовами оптимизации в таких вещах, как Is ‹ быстрее, чем ‹= ? Но в данном случае это на самом деле вызов функции, что значительно дороже. - person Mysticial; 05.02.2013
comment
@Ник Бугалис Спасибо! Я еще не тестировал его. Это только мое чувство. Что вы предлагаете в такой ситуации, когда решение о том, какую функцию вызывать, нужно решать во время выполнения? - person Zhangyi Hu; 05.02.2013
comment
Мое предложение состоит в том, чтобы помечать функции, которые маленькие и очень часто вызываются, с помощью inline и позволить компилятору решать, что имеет смысл встраивать. Затем вы можете профилировать код и соответствующим образом настроить/оптимизировать его. Помните, что встраивание на самом деле имеет и недостатки — оно может значительно увеличить размер кода, уменьшить коэффициент попаданий в кэш и т. д. Так что это не панацея. @Mysticial очень хорошо разбирается в оптимизации, поэтому он может также захотеть вмешаться. - person Nik Bougalis; 05.02.2013
comment
На самом деле я не полагаюсь на inline. Я использую его только как подсказку, если думаю, что это может иметь значение. В тех случаях, когда мне абсолютно необходимо, чтобы это было встроено (потому что я знаю, что это важно), я злоупотребляю макросами. Я даже не беспокоюсь о принудительно встроенных прагмах, потому что я видел достаточно случаев, когда компиляторы не могли должным образом оптимизировать передачу всех параметров. (так что в результате получается куча ненужных данных-копий и конвертаций) - person Mysticial; 05.02.2013
comment
@Mysticial Меня зовут Ник, и я тоже злоупотребляю макросами для повышения производительности... ;) - person Nik Bougalis; 05.02.2013

Если код решает во время выполнения, какую функцию вызывать, ясно, что функция НЕ МОЖЕТ быть встроена — нет другого выбора, кроме как вызывать ее через указатель. Это действительно случай «может ли компилятор понять, что происходит, или нет». В случае, когда вы вызываете функцию через указатель на основе некоторых условий, компилятору необходимо понять, как условия влияют на то, какой указатель используется. Если компилятор не может решить это в момент компиляции кода, он ДОЛЖЕН использовать указатель на функцию.

Таким образом, в вашем примере кода компилятор может (если захочет) встроить как foo, так и рисунок f.

Но в случае, скажем,

В конструкторе, например:

if (x > y) pf = foo::f(); else pf = foo::g();

В каком-то другом коде, где компилятор не имеет прямых знаний о том, какие значения x и y были в конструкции:

*blah.*pf(); 

вызов не может быть встроен, потому что компилятор не знает, является ли функция f или g вызываемой.

Я надеюсь это имеет смысл.

[Мне также интересно, почему вы не можете использовать виртуальные функции...]

person Mats Petersson    schedule 05.02.2013

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

person David Rodríguez - dribeas    schedule 05.02.2013

«Эти функции будут часто и неоднократно вызываться в цикле, поэтому для повышения производительности они должны быть встроены».

такого в наше время нет.

для вашего встроенного ответа: этот вызов будет встроенным или не будет зависеть от компилятора.

person Arpit    schedule 05.02.2013
comment
Это просто неправда - есть законные случаи, когда встроенный код может помочь, часто очень сильно. Но их очень и очень мало. - person Nik Bougalis; 05.02.2013
comment
такого в наше время нет. - Позволю себе не согласиться. В некоторых случаях это имеет значение. - person Mysticial; 05.02.2013
comment
это не относится к ОП, я уверен в этом. - person Arpit; 05.02.2013
comment
@Mysticial, поскольку вы более опытны, вы лучше знаете, и я уважаю это, и у вас должен быть опыт. но я действительно не видел такого кода, только в теории я изучал это. :) - person Arpit; 05.02.2013
comment
@NikBougalis, если у вас есть пример кода, поделитесь ссылкой, если можете. - person Arpit; 05.02.2013
comment
@Arpit Это редко, но может иметь значение. Если это произойдет в критическом цикле и у функции будет всего несколько дополнений, то это будет иметь большое значение. Тем не менее, это много если. Но есть достаточно пользователей, которые сначала создают профиль, прежде чем спрашивать. - person Mysticial; 05.02.2013