0

I ran some code to monitor Pi hysteresis which involved mirroring a GPIO input to output. I was surprised as how slow the code was, so wrote a C version. Although this was slightly better it was still unacceptably slow.

Just how fast are the various libraries?

asked Aug 7, 2021 at 2:42

1 Answer 1

3

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.

answered Aug 7, 2021 at 2:42
6
  • FYI, I've posted a clip from your answer here Commented Aug 8, 2021 at 0:26
  • @Seamus I have been playing with my own library, still very much in development. Ultimately I intend to create a shared C library. This shows pi.gpio 36764706 toggles per second NOTE I have some scepticism about the timing algorithm I am using. Commented Aug 8, 2021 at 4:16
  • Thank you for documenting this. The results of WiringPi are admirable but would you compare to mapping gpiomem as well? Commented Aug 11, 2021 at 15:59
  • @MaxDZ8 Most of these libraries use /dev/gpiomem Commented Aug 12, 2021 at 1:12
  • @Milliways yes I have noticed, it's very clearly marked on your message, as well as a wild difference on performance. Given the data it seems reasonable to me direct access might be different as well (I use direct access and as far as I recall the performance is very different). Commented Aug 12, 2021 at 9:34

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.