Stack Overflow Asked by Victor CANOZ on August 7, 2020
Today I’ve wanted to check if it is possible to write C++ code that is as efficient as C code. Because I’m working on an embedded system, I want to make sure using C++ will not impact performance too much.
So I’ve written a C code for manipulating fake GPIOs registers, as well as its C++ counterpart.
#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;
}
equivalent C++ code:
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;
}
If I enable -O3 optimization, both source codes result in exactly the same compiler output, which is great. You can check by yourself here:
https://godbolt.org/z/P7ov8E for C++, and here https://godbolt.org/z/Kav6qb for C.
Now assume I need to iterate through the gpios to initialize a bunch of them as inputs. In C it is quite easy, and I would add this to the main() function:
for (int i =0; i<10;i++)
{
gpio_init_input(PUSH_BUTTON_PORT, i);
}
For the C++ version, I had to re-write the class because it was a templated class, and you can’t easily iterate through multiples types of a class.
So here is the new C++ code:
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;
}
As you can see here https://godbolt.org/z/dzMnds for the C++ version and here https://godbolt.org/z/4czfKx for the C version, the C++ compiler output is about 25% bigger than the C counterpart.
So is their a way of using a C++ class in this case without compromising efficiency (over C)?
The following change helps
class gpio
{
const unsigned int port;
const unsigned int pin;
public:
gpio(unsigned int port, unsigned int pin) : port(port), pin(pin)
{
}
By declaring the member variables as const
(and consequently using an initialisation list in the constructor) it helps the compiler optimise their use.
I'm no expert on reading assembly, but that seems to make the two programs compile to equivalent code.
Correct answer by john on August 7, 2020
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP