Как изменить этот класс C ++, чтобы он был таким же эффективным, как код C? - PullRequest
0 голосов
/ 03 августа 2020

Сегодня я хотел проверить, можно ли написать код на C ++, столь же эффективный, как код C. Поскольку я работаю над встроенной системой, я хочу убедиться, что использование C ++ не слишком сильно повлияет на производительность. Итак, я написал код C для управления поддельными регистрами GP IOs, а также его аналог на C ++.

#include <stdbool.h>
#define PUSH_BUTTON_PORT 0
#define PUSH_BUTTON_PIN 4

volatile int port0_pin_dir= 0;
volatile int port0_pin_read = 0;

void inline gpio_init_input(unsigned int port, unsigned int pin)
{
    switch (port)
    {
        case 0:
            port0_pin_dir |= 1 << pin;
        break;
        case 1:
            //manage port 1 direction register...
        break;
    }
}

bool inline gpio_read(unsigned int port, unsigned int pin)
{
    switch (port)
    {
        case 0:
            return (port0_pin_read >> pin) & 1;
        case 1:
            //TODO: manage port 1 direction register...
            return 0;
    }
}

volatile bool is_pressed = 0;
int main()
{
    gpio_init_input(PUSH_BUTTON_PORT, PUSH_BUTTON_PIN);
    is_pressed = gpio_read(PUSH_BUTTON_PORT, PUSH_BUTTON_PIN);
    return 0;
}

эквивалентный код C ++:

volatile int port0_pin_dir = 0;
volatile int port0_pin_read = 0;

template<unsigned int PORT, unsigned int PIN>
class gpio
{
public:
    gpio()
    {
        static_assert(PORT < 2, "This chip has only 2 gpio ports");
        static_assert(PIN < 32, "This pins has 32 pins per port");
    }

    void init_input()
    {
        switch (PORT)
        {
            case 0:
                port0_pin_dir |= 1 << PIN;
            break;
            case 1:
                //TODO: manage port 1 direction register...
            break;
        }
    }

    bool read()
    {
        switch (PORT)
        {
            case 0:
                return (port0_pin_read >> PIN) & 1;
            case 1:
                //TODO: manage port 1 direction register...
                return 0;
        }
        
    }
};

volatile bool is_pressed = false;
int main()
{
    gpio<0,4> push_button;
    push_button.init_input();
    is_pressed = push_button.read();
    return 0;
}

Если я включить оптимизацию -O3, оба исходных кода дают точно такой же вывод компилятора, и это здорово. Вы можете проверить самостоятельно здесь: https://godbolt.org/z/P7ov8E для C ++, а здесь https://godbolt.org/z/Kav6qb для C.

Теперь предположим, что мне нужно выполнить итерацию через gp ios для инициализации группы из них в качестве входных. В C это довольно просто, и я бы добавил это к функции main ():

for (int i =0; i<10;i++)
{
    gpio_init_input(PUSH_BUTTON_PORT, i);
}

Для версии C ++ мне пришлось переписать класс, потому что это был шаблонный класс, и вы не можете легко перебирать несколько типов класса.

Итак, вот новый код C ++:

volatile int port0_pin_dir = 0;
volatile int port0_pin_read = 0;

class gpio
{
    unsigned int port;
    unsigned int pin;
public:
    gpio(unsigned int port, unsigned int pin)
    {
        port = port;
        pin = pin;
    }

    void init_input()
    {
        switch (port)
        {
            case 0:
                port0_pin_dir |= 1 << pin;
            break;
            case 1:
                //TODO: manage port 1 direction register...
            break;
        }
    }

    bool read()
    {
        switch (port)
        {
            case 0:
                return (port0_pin_read >> pin) & 1;
            case 1:
                //TODO: manage port 1 direction register...
                return 0;
        }
        
    }
};

volatile bool is_pressed = false;
int main()
{
    gpio push_button(0,4);
    push_button.init_input();
    is_pressed = push_button.read();

    for (int i =0; i<10;i++)
    {
        gpio pin(0,i);
        pin.init_input();
    }

    return 0;
}

Как вы можете видеть здесь https://godbolt.org/z/dzMnds для версии C ++ и здесь https://godbolt.org/z/4czfKx для версии C, вывод компилятора C ++ примерно на 25% больше, чем у C аналога.

Так это их путь использования класса C ++ в этом случае без ущерба для эффективности (более C)?

1 Ответ

1 голос
/ 03 августа 2020

Следующее изменение помогает

class gpio
{
    const unsigned int port;
    const unsigned int pin;
public:
    gpio(unsigned int port, unsigned int pin) : port(port), pin(pin)
    {
    }

Объявляя переменные-члены как const (и, следовательно, используя список инициализации в конструкторе), это помогает компилятору оптимизировать их использование.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...