I wrote programs for a number of libraries, both c and python.
These just toggle a GPIO pin as fast as possible.
NOTE In case anyone misunderstands I am aware this is an artificial test with no real world use and there are far better (and faster) ways of generating a "square" wave. It is indicative of the response to programmed GPIO commands.
pigpio is quite slow; this is to be expected due to the socket interface which has significant overhead.
WiringPI is extremely fast (I would expect the BCM2835 to perform similarly although I did not test). This is expected as they directly access GPIO registers.
RPi.GPIO is very fast - the underlying /dev/gpiomem code is written in c to directly access GPIO registers.
lgpio C library allows control of the GPIO pins through the gpiochip kernel interface is also very fast.
WiringPi 7930214 toggles per second
RPi.GPIO 801556 toggles per second
lgpio 523049 toggles per second
pigpiod C 7014 toggles per second
pigpiod Python 5974 toggles per second
This post has been referenced elsewhere and (as I feared) misinterpreted. If I wanted a program which runs as fast as possible I would not use any library.
Joan has pointed out pigpiod
uses a client server model and each toggle requires a couple of network messages which accounts for the dramatic performance difference. It would be more sensible to compare against pigpio
.
I did not include pigpio
for the simple reason that I do not use it. It is incompatible with pigpiod
(which I do use) and only allows a single program to run at a time.
I have added pigpio
code which is much faster (even when run on a different model Pi).
pigpio C 2905649 toggles per second
The code is listed below:-
/*
This program toggles a GPIO pin and measures rate.
Using wiringPi
TO BUILD
gcc -Wall -o bench-wiringPi bench-wiringPi.c -lwiringPi
sudo ./bench-wiringPi
*/
// 2021-08-06
#define SigOUT 12
#define LOOPS 20000
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <wiringPi.h>
int main(void) {
unsigned t0, t1;
unsigned i;
wiringPiSetupGpio();
pinMode(SigOUT, OUTPUT);
t0 = micros();
for (i = 0; i < LOOPS + 1; i++) {
digitalWrite(SigOUT, HIGH);
digitalWrite(SigOUT, LOW);
}
t1 = micros();
printf("WiringPi\t%10.0f toggles per second\n", (1.0e6 * LOOPS) / (t1 - t0));
return 0;
}
/*
bench.c
This program toggles a GPIO pin and measures rate.
Using lgpio C API
gcc -Wall -o bench-lgpio bench-lgpio.c -llgpio
./bench
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <lgpio.h>
#define LFLAGS 0
#define SigOUT 12
#define LOOPS 20000
int main(int argc, char *argv[]) {
int h;
int i;
double t0, t1;
int status;
h = lgGpiochipOpen(0);
if (h >= 0) {
if ((status = lgGpioClaimOutput(h, LFLAGS, SigOUT, 0)) == LG_OKAY) {
t0 = lguTime();
for (i = 0; i < LOOPS; i++) {
lgGpioWrite(h, SigOUT, 0);
lgGpioWrite(h, SigOUT, 1);
}
t1 = lguTime();
printf("lgpio\t\t%10.0f toggles per second\n", (1.0 * LOOPS) / (t1 - t0));
} else {
printf("lgGpioClaimSigOUTput FAILED on Pin %d\n", SigOUT);
lgLineInfo_t lInfo;
status = lgGpioGetLineInfo(h, SigOUT, &lInfo);
if (status == LG_OKAY) {
if (lInfo.lFlags & 1) {
printf("GPIO in use by the kernel ");
printf("name=%s user=%s\n", lInfo.name, lInfo.user);
}
}
}
lgGpioFree(h, SigOUT);
lgGpiochipClose(h);
} else
printf("Unable to open gpiochip 0\n");
}
#! /usr/bin/env python3
"""
This program toggles a GPIO pin and measures rate.
Using RPi.GPIO Python Interface
"""
import RPi.GPIO as GPIO
import time
from signal import pause
SigOUT = 12
LOOPS = 20000
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(SigOUT, GPIO.OUT)
t0 = time.time()
for i in range(LOOPS):
GPIO.output(SigOUT,0)
GPIO.output(SigOUT,1)
t1 = time.time()
print("RPi.GPIO\t{:>10.0f} toggles per second".format((1.0 * LOOPS) / (t1 - t0)))
GPIO.cleanup()
/*
This program toggles a GPIO pin and measures rate.
Using pigpiod C Interface
TO BUILD
gcc -Wall -o bench-pigpiod bench-pigpiod.c -lpigpiod_if2
*/
// 2021-09-21
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pigpiod_if2.h>
char *optHost = NULL;
char *optPort = NULL;
int SigOUT = 12;
#define LOOPS 20000
void usage() {
fprintf(stderr,
"\n"
"Usage: bench-pigpio [OPTION] ...\n"
" -o value, output pin, 0-31, default %d\n"
" -h string, host name, default NULL\n"
" -p value, socket port, 1024-32000, default 8888\n"
"EXAMPLE\n"
"bench-pigpio -i20 -o26\n",
SigOUT);
}
static uint64_t getNum(char *str, int *err) {
uint64_t val;
char *endptr;
*err = 0;
val = strtoll(str, &endptr, 0);
if (*endptr) {
*err = 1;
val = -1;
}
return val;
}
static void initOpts(int argc, char *argv[]) {
int opt, err;
while ((opt = getopt(argc, argv, "g:h:i:m:p:")) != -1) {
switch (opt) {
case 'o':
SigOUT = getNum(optarg, &err);
break;
case 'h':
optHost = malloc(sizeof(optarg) + 1);
if (optHost)
strcpy(optHost, optarg);
break;
optPort = malloc(sizeof(optarg) + 1);
if (optPort)
strcpy(optPort, optarg);
break;
default: /* '?' */
usage();
exit(-1);
}
}
}
int main(int argc, char *argv[]) {
int i;
int pi;
double t0, t1;
initOpts(argc, argv);
pi = pigpio_start(optHost, optPort); /* Connect to Pi. */
if (pi >= 0) {
set_mode(pi, SigOUT, PI_OUTPUT);
t0 = time_time();
for (i = 0; i < LOOPS; i++) {
gpio_write(pi, SigOUT, 0);
gpio_write(pi, SigOUT, 1);
}
t1 = time_time();
printf("pigpio C\t%10.0f toggles per second\n", (1.0 * LOOPS) / (t1 - t0));
pigpio_stop(pi); /* Disconnect from Pi. */
}
return 0;
}
#! /usr/bin/env python3
"""
This program toggles a GPIO pin and measures rate.
Using pigpiod Python Interface
"""
import pigpio
import time
from signal import pause
SigOUT = 12
LOOPS = 20000
pi = pigpio.pi()
pi.set_mode(SigOUT, pigpio.OUTPUT)
t0 = time.time()
for i in range(LOOPS):
pi.write(SigOUT,0)
pi.write(SigOUT,1)
t1 = time.time()
print("pigpiod Python\t{:>10.0f} toggles per second".format((1.0 * LOOPS) / (t1 - t0)))
pi.stop()
/*
This program toggles a GPIO pin and measures rate.
Using pigpio C Interface
TO BUILD
gcc -Wall -o bench-pigpio bench-pigpio.c -lpigpio
*/
// 2021-09-21
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pigpio.h>
int SigOUT = 12;
#define LOOPS 20000
int main(int argc, char *argv[]) {
int i;
double t0, t1;
if(gpioInitialise() < 0) return 1;
gpioSetMode(SigOUT, PI_OUTPUT);
t0 = time_time();
for (i = 0; i < LOOPS; i++) {
gpioWrite(SigOUT, 0);
gpioWrite(SigOUT, 1);
}
t1 = time_time();
printf("pigpio C\t%10.0f toggles per second\n", (1.0 * LOOPS) / (t1 - t0));
return 0;
}
I was asked what Pi I used. I can't remember, as I ran this code on a number of Pi (including a Pi3+ running Bullseye). The results above were probably on a Pi 4 Model B Rev 1.1, but there wasn't much difference.
While investigating GPIO hysteresis I ran a program which used the GPIO as a Schmitt trigger Hysteresis - on or off?.
This produced erratic results and was quite slow, indicating significant overhead in pigpiod. I wrote an assembler program to do similar.
I then decided to investigate the overheads of various libraries and the results are above.
The faster results were quite erratic (as expected) and depended more on what else the OS was doing.