优化已移植到 Python 的 C 算法
Optimising a C Algorithm having ported to Python
我想在 Python 中编写一个实时音频到音调算法,看起来 Yin 算法解决了这个问题。我找到了许多 Yin 的 C 实现,并在我的声音中尝试了其中一个,它按预期实时工作。我已经开始将它移植到 Python,但我可以看到它慢了大约 100 倍 - 所以不再是 real.time。我使用的是 2.3 GHz 四核英特尔酷睿 i7。
下面是在 C 和 Python 中对该算法的模拟,以了解所涉及的计算。每个模拟都在计算 90,000 个样本的音调。 C 函数需要 2 秒,Python 需要 200 秒才能完成。
C代码
#include <stdio.h>
#include <stdint.h>
#include <time.h>
int main(int argc, char** argv) {
int buffer_length = 150;
float sec = 0;
float* buffer = malloc(sizeof(float)*buffer_length);
clock_t before = clock();
int j;
int16_t tau;
int16_t i;
float delta;
for(j = 0; j < 90000; j++){
for(tau = 0 ; tau < 75; tau++){
for(i = 0; i < 75; i++){
delta = buffer[i] - buffer[i + tau];
buffer[tau] += delta * delta;
}
}
}
clock_t difference = clock() - before;
sec = difference / (float )CLOCKS_PER_SEC;
printf("%f\n",sec);
return 0;
}
Python代码
import time
buffer = [0.0]*150
ts = time.time()
for j in range(90000):
for tau in range(75):
for i in range(75):
delta = buffer[i] - buffer[i + tau]
buffer[tau] += delta * delta
print(time.time()-ts)
我正在考虑使用 8KHz 的采样率,但如果可能的话可能会更高。是否可以大大提高 Python 代码的性能?如果可以,我将如何着手这样做?如果没有,那么我想用 Python 包装器编写一个 C 库是我唯一的选择。
这看起来像是 numba 的工作。问题是 python for 循环比 C 循环 显着 慢。使用 numba 你应该能够非常显着地加快它们的速度,因为你自己编写了它们并且没有外部依赖项(众所周知,它们会扰乱 numba)。
只需使用 njit 装饰器并将嵌套的 for 循环包装在一个函数中。
是的,Python 在许多应用程序中比 C 慢得多。那些需要大量数值计算的是最糟糕的情况。有多种方法可以解决此类问题。
直接在 C 中编写扩展是 old-school 方式,它可能对您特别有吸引力,因为您已经有了 C 代码。有一些工具可以帮助解决这个问题,例如 swig。
或者,您可以使用 Numpy 充分表达所需的计算。或者有 Cython,用于编写算法版本或包装它的 C 实现。
您要做的关键是避免在本机和 Python 数字表示之间进行转换。保持所有数字的原生。
我想在 Python 中编写一个实时音频到音调算法,看起来 Yin 算法解决了这个问题。我找到了许多 Yin 的 C 实现,并在我的声音中尝试了其中一个,它按预期实时工作。我已经开始将它移植到 Python,但我可以看到它慢了大约 100 倍 - 所以不再是 real.time。我使用的是 2.3 GHz 四核英特尔酷睿 i7。
下面是在 C 和 Python 中对该算法的模拟,以了解所涉及的计算。每个模拟都在计算 90,000 个样本的音调。 C 函数需要 2 秒,Python 需要 200 秒才能完成。
C代码
#include <stdio.h>
#include <stdint.h>
#include <time.h>
int main(int argc, char** argv) {
int buffer_length = 150;
float sec = 0;
float* buffer = malloc(sizeof(float)*buffer_length);
clock_t before = clock();
int j;
int16_t tau;
int16_t i;
float delta;
for(j = 0; j < 90000; j++){
for(tau = 0 ; tau < 75; tau++){
for(i = 0; i < 75; i++){
delta = buffer[i] - buffer[i + tau];
buffer[tau] += delta * delta;
}
}
}
clock_t difference = clock() - before;
sec = difference / (float )CLOCKS_PER_SEC;
printf("%f\n",sec);
return 0;
}
Python代码
import time
buffer = [0.0]*150
ts = time.time()
for j in range(90000):
for tau in range(75):
for i in range(75):
delta = buffer[i] - buffer[i + tau]
buffer[tau] += delta * delta
print(time.time()-ts)
我正在考虑使用 8KHz 的采样率,但如果可能的话可能会更高。是否可以大大提高 Python 代码的性能?如果可以,我将如何着手这样做?如果没有,那么我想用 Python 包装器编写一个 C 库是我唯一的选择。
这看起来像是 numba 的工作。问题是 python for 循环比 C 循环 显着 慢。使用 numba 你应该能够非常显着地加快它们的速度,因为你自己编写了它们并且没有外部依赖项(众所周知,它们会扰乱 numba)。
只需使用 njit 装饰器并将嵌套的 for 循环包装在一个函数中。
是的,Python 在许多应用程序中比 C 慢得多。那些需要大量数值计算的是最糟糕的情况。有多种方法可以解决此类问题。
直接在 C 中编写扩展是 old-school 方式,它可能对您特别有吸引力,因为您已经有了 C 代码。有一些工具可以帮助解决这个问题,例如 swig。
或者,您可以使用 Numpy 充分表达所需的计算。或者有 Cython,用于编写算法版本或包装它的 C 实现。
您要做的关键是避免在本机和 Python 数字表示之间进行转换。保持所有数字的原生。