Будет ли компилятор оптимизировать неиспользуемые аргументы статической функции?

У меня есть группа функций, которые объявлены как static и fastcall. Большинство из них используют указатель на структуру, которая более или менее выполняет роль this в C++. Некоторым функциям ничего не нужно в структуре, но ради единообразия я все равно хочу передать им указатель. Заметит ли компилятор, что аргумент не используется, и пропустит выделение для него регистра?


person cleong    schedule 12.01.2013    source источник


Ответы (3)


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

#include <stdio.h>
#include <vector>
#include <algorithm>

using namespace std;

struct demo
{
    int a;
    char ch;
};

static  void __fastcall func(struct demo* d)
{
    for(int i = 0; i < 100; i++) {
    std::vector<int> a;
    std::sort(begin(a), end(a));
    printf("THis is a test");
    printf("Hello world\n");
    }
}

int main()
{
  //  void*p = (void*)&func;
    struct demo d;
    func(&d);
    func(&d);
    func(&d);
    func(&d);
    func(&d);
    func(&d);
    func(&d);
    //printf((const char*)p);
}

Как написано там, функция вызывает компиляцию для этого: -

    func(&d);
00F61096  call        func (0F61000h)  
    func(&d);
00F6109B  call        func (0F61000h)  
    func(&d);
00F610A0  call        func (0F61000h)  
    func(&d);
00F610A5  call        func (0F61000h)  
    func(&d);
00F610AA  call        func (0F61000h)  
    func(&d);
00F610AF  call        func (0F61000h)  
    func(&d);
00F610B4  call        func (0F61000h)  

Что показывает, что компилятор действительно пропустит параметр, если он не используется. Однако если я раскомментирую две строки, чтобы взять адрес функции, тогда все изменится, вместо этого будет сгенерировано это: -

00C71099  lea         ecx,[esp]  
00C7109C  call        func (0C71000h)  
    func(&d);
00C710A1  lea         ecx,[esp]  
00C710A4  call        func (0C71000h)  
    func(&d);
00C710A9  lea         ecx,[esp]  
00C710AC  call        func (0C71000h)  
    func(&d);
00C710B1  lea         ecx,[esp]  
00C710B4  call        func (0C71000h) 

Куда он ДЕЙСТВИТЕЛЬНО отправляет указатель.

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

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

person jcoder    schedule 12.01.2013
comment
имеет смысл, что получение адреса функции не позволяет оптимизировать неиспользуемые параметры, потому что указатель функции типизируется по сигнатуре функции (даже если вы приводите его к void*), и компилятор, как правило, не может быть конечно, если вы используете его снова с помощью какой-то странной магии. +1 за указание на эту проблему - person Andreas Grapentin; 12.01.2013
comment
Конечно. Я действительно задавался вопросом, сможет ли компилятор с генерацией кода во время компоновки использовать пользовательское соглашение о вызовах в любом случае, поскольку он может видеть весь код, но, по-видимому, нет, по крайней мере, в случае моего теста. - person jcoder; 12.01.2013
comment
Главный вывод, который я сделал из своего теста, — не полагайтесь на это в любом случае. Он может передавать или не передавать параметр в регистр. - person jcoder; 12.01.2013
comment
Ваш тест показывает, что вызывающий объект не заполняет регистр, но он не показывает, выделил ли он его или нет (т. е. менее ли подвержены последующие аргументы попаданию в стек) - person JasonD; 12.01.2013
comment
Большое спасибо. Я думаю, что в целом компилятор воздержится от изменения соглашения о вызове, когда адрес функции берется, потому что указатель может переходить на разные функции. - person cleong; 12.01.2013

Прежде всего, компилятор должен предупредить вас о неиспользуемых аргументах функции, если вы используете правильные флаги компилятора (например, -Wall). Я бы рекомендовал оставить аргументы, которые вы не используете, вне списка аргументов, хотя я думаю, они будут оптимизированы. Что вы можете сделать, чтобы убедиться, однако, скомпилируйте свой код дважды, один раз с аргументами и один раз без, и выполните diff между двоичными файлами и посмотрите, совпадают ли они. Я думаю, это должно зависеть от используемого вами компилятора и флагов оптимизации.

Ваше здоровье,

Энди

person Andreas Grapentin    schedule 12.01.2013
comment
согласился, что компилятор оптимизирует его, но проверка его с помощью diff - нельзя будет проверить различия (поскольку выполнение зависит от многих других вещей, которые могут различаться для каждого процесса выполнения, кроме того, инициализация переменной не займет много времени - person exexzian; 12.01.2013
comment
@sansix, если двоичные файлы равны, почему поведение должно отличаться? - person Andreas Grapentin; 12.01.2013
comment
Кстати - дифф ч/б бинарники чего? (может быть, я думаю о другом) - person exexzian; 12.01.2013
comment
@sansix извините, я не понимаю вашего вопроса. - person Andreas Grapentin; 12.01.2013
comment
ваше это утверждение do a diff between the binaries - я спрашиваю diff b/w двоичные файлы чего? время выполнения или ?? - person exexzian; 12.01.2013
comment
@sansix diff — это инструмент unix для сравнения файлов. Я предложил сравнить скомпилированные программы, чтобы увидеть, соответствует ли сгенерированный код (двоичный файл 1) с неиспользуемыми аргументами функции сгенерированному коду (двоичный файл 2) без неиспользуемых аргументов. это должен быть надежный способ узнать, были ли они оптимизированы. - person Andreas Grapentin; 12.01.2013
comment
О, теперь понял, да, это действительно будет хороший подход. +1 - person exexzian; 12.01.2013
comment
Нет никакой гарантии, что какое-либо различие, обнаруженное в двоичных файлах, связано с тем, что аргумент был опущен, поэтому я не думаю, что это вообще надежно. - person JasonD; 12.01.2013
comment
@JasonD Я согласен, что разница мало что вам скажет. однако я ожидаю увидеть равенство, и это будет довольно убедительный результат. :) - person Andreas Grapentin; 13.01.2013

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

void func(struct demo *) { ... }

person sameer chaudhari    schedule 10.02.2017