与 C 相比,为什么在 Python 中写入 I2C 太慢?

Fom*_*aut 5 python gcc ctypes i2c raspberry-pi

我试图找出为什么当我尝试写入 I2C 时,即使我使用 CDLL,Python 中的相同代码的工作速度也比 C 慢 25 倍。下面我将逐步描述我正在做的所有细节。

树莓派版本:Raspberry PI 3 Model B

操作系统:Raspbian Buster Lite 版本:2019 年 7 月

GCC 版本:gcc (Raspbian 8.3.0-6+rpi1) 8.3.0

Python 版本:Python 3.7.3

尽管 I2C,我正在使用的设备是 MCP23017。我所做的就是将 0 和 1 写入引脚 B0。这是我用 C 编写的代码:

// test1.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <time.h>


int init() {
    int fd = open("/dev/i2c-1", O_RDWR);
    ioctl(fd, I2C_SLAVE, 0x20);
    return fd;
}


void deinit(int fd) {
    close(fd);
}


void makewrite(int fd, int v) {
    char buffer[2] = { 0x13, 0x00 };
    buffer[1] = v;
    write(fd, buffer, 2);
}


void mytest() {
    clock_t tb, te;
    int n = 1000;

    int fd = init();

    tb = clock();
    int v = 1;
    for (int i = 0; i < n; i++) {
        makewrite(fd, v);
        v = 1 - v;
    }
    te = clock();

    printf("Time: %.3lf ms\n", (double)(te - tb) / n / CLOCKS_PER_SEC * 1e3);

    deinit(fd);
}


int main() {
    mytest();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我使用以下命令编译并运行它:

gcc test1.c -o test1 && ./test1
Run Code Online (Sandbox Code Playgroud)

它给了我结果:

pi@raspberrypi:~/dev/i2c_example $ gcc test1.c -o test1 && ./test1
Time: 0.020 ms
Run Code Online (Sandbox Code Playgroud)

我可以得出结论,写入引脚需要 0.02 毫秒。

之后,我创建 SO 文件以便能够从我的 Python 脚本访问编写的函数:

gcc -c -fPIC test1.c -o test1.o && gcc test1.o -shared -o test1.so
Run Code Online (Sandbox Code Playgroud)

还有我要测试的 Python 脚本:

# test1.py

import ctypes
from time import time

test1so = ctypes.CDLL("/home/pi/dev/i2c_example/test1.so")

test1so.mytest()

n = 1000

fd = test1so.init()

tb = time()
v = 1
for _ in range(n):
    test1so.makewrite(fd, v)
    v = 1 - v
te = time()
print("Time: {:.3f} ms".format((te - tb) / n * 1e3))

test1so.deinit(fd)
Run Code Online (Sandbox Code Playgroud)

这为我提供了结果:

pi@raspberrypi:~/dev/i2c_example $ python test1.py
Time: 0.021 ms
Time: 0.516 ms
Run Code Online (Sandbox Code Playgroud)

我不明白为什么makewrite在 Python 中调用25 倍,尽管实际上我调用了相同的 C 代码。我也研究了,如果我的评论write(fd, buffer, 2);test1.c或更改fd1,由Python脚本给出的时间是兼容的,有没有这样的巨大差异。

// in test1.c
write(fd, buffer, 2); -> write(1, buffer, 2);
Run Code Online (Sandbox Code Playgroud)

运行 C 程序:

pi@raspberrypi:~/dev/i2c_example $ gcc test1.c -o test1 && ./test1
...Time: 0.012 ms
Run Code Online (Sandbox Code Playgroud)

运行 Python 程序:

pi@raspberrypi:~/dev/i2c_example $ python3 test1.py 
...Time: 0.009 ms
...Time: 0.021 ms
Run Code Online (Sandbox Code Playgroud)

这让我很困惑。谁能告诉我为什么会发生这种情况,以及如何使用 C-DLL 通过 I2C 提高我在 Python 中的性能?

概括:

描述符:1(标准输出)

纯 C 语言 makewrite 的执行时间:0.009 ms

从 Python 调用 C-DLL 函数的 C 中 makewrite 的执行时间:0.021 毫秒

结果可想而知。这个差别并没有那么大。可以解释为,Python 循环及其语句不如 C 中的高效,因此增加了执行时间。

描述符:I2C

纯 C 语言 makewrite 的执行时间:0.021 ms

从 Python 调用作为 DLL 函数的 C 中 makewrite 的执行时间:0.516 ms

文件描述符切换到I2C纯C周围增加了执行时间后0.012 ms,所以我期望的执行时间从Python的呼唤:0.021 ms + 0.012 ms = 0.033 ms,因为所有的改变我已经做在里面的makewrite,所以Python应该不知道这个内部东西(因为它打包在 so-file 中)。但我有,0.516 ms而不是0.033 ms让我感到困惑。