为什么在Python中调用C函数时性能会下降?

问题描述

我正在使用Daniel Lemire建议的随机生成器。使用gcc -O3进行编译时,它在C中非常有效。只需 3.9秒即可生成 10 ^ 9 随机数。但是,当我在Python中调用C函数时,它慢了大约 100倍

这是C代码

#include <stdint.h>
 
static __uint128_t g_lehmer64_state = (__uint128_t)513543212345676543;

uint64_t lehmer64() {
  g_lehmer64_state *= 0xda942042e4dd58b5ull;
  return g_lehmer64_state >> 64;
}

但是,我想在Python中使用此C函数。因此,我使用GCC创建了一个库:

gcc -o liblehmer64.so -shared -fPIC -O3 rand_test.c

然后,我使用ctypes模块并在Python中测试性能

from ctypes import c_uint64,CDLL

lib = CDLL('liblehmer64.so')

lehmer64 = lib.lehmer64
lehmer64.restype = c_uint64

for i in range (1000000000):
  rnd = lehmer64()

问题

解决方法

为什么Python代码比C代码慢100倍?

因为调用该函数有很多事情要做。函数调用本身很昂贵。

如何提高Python代码的性能?

如果您想要庞大的随机数列表,则可以编写一个C函数来返回庞大的列表,从而最大程度地减少调用次数。

例如,您可以在python中实现一个存储10000个随机数的队列。您可以使用上述C函数对其进行初始化。然后,每次您从队列中弹出最后一个数字时,队列都会自动调用该函数。如果那也太慢,那么也许您也应该使用C中的随机数来编写函数。在Python中创建庞大的列表非常昂贵。

一个通过编写C函数而受益匪浅的例子是is_prime(x)函数,也就是说,如果x是质数,则返回true,否则返回false。特别是如果我们谈论的是非常大的数字。这种情况具有以下两个关键属性:

  • is_prime(x)函数之间来回的数据很少。您将其发送给一个简单的整数并返回一个布尔值。
  • 对于大型x,这是非常繁重的操作

我创建队列的示例在第一点失败。在C和Python之间会发送大量数据。

您的功能在第二点失败。与函数调用开销相比,该计算非常轻巧。