LLVM 编译器在本机 C 代码上的性能与 Python+Numba 的对比
Performance of LLVM-Compiler on native C code vs Python+Numba
最近在Python做了一些性能优化方面的测试。其中一部分是使用 SWIG 对 Monte-Carlo Pi 计算进行基准测试,并编译一个库以导入 Python。另一个解决方案是使用 Numba。现在我完全想知道为什么原生 C 解决方案比 Numba 更糟糕,即使 LLVM 编译器都用于两者。所以我想知道我是否做错了什么。
在我的笔记本电脑上运行
native C module: 7.09 s
Python+Numba: 2.75 s
原生 C 代码
#include "swigtest.h"
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
float monte_carlo_pi(long nsamples)
{
int accGlob=0;
int accLoc=0;
int i,ns;
float x,y;
float res;
float iRMX=1.0/(float) RAND_MAX;
srand(time(NULL));
for(i=0;i<nsamples;i++)
{
x = (float)rand()*iRMX;
y = (float)rand()*iRMX;
if((x*x + y*y) < 1.0) { acc += 1;}
}
res = 4.0 * (float) acc / (float) nsamples;
printf("cres = %.5f\n",res);
return res;
}
swigtest.i
%module swigtest
%{
#define SWIG_FILE_WITH_INIT
#include "swigtest.h"
%}
float monte_carlo_pi(long nsamples);
编译器调用
clang.exe swigtest.c swigtest_wrap.c -Ofast -o _swigtest.pyd -I C:\python37\include -shared -L c:\python37\libs -g0 -mtune=intel -msse4.2 -mmmx
testswig.py
from swigtest import monte_carlo_pi
import time
import os
start = time.time()
pi = monte_carlo_pi(250000000)
print("pi: %.5f" % pi)
print("tm:",time.time()-start)
Python 带有 Numba 的版本
from numba import jit
import random
import time
start = time.time()
@jit(nopython=True,cache=True,fastmath=True)
def monte_carlo_pi(nsamples: int)-> float:
acc:int = 0
for i in range(nsamples):
x:float = random.random()
y:float = random.random()
if (x * x + y * y) < 1.0: acc += 1
return 4.0 * acc / nsamples
pi = monte_carlo_pi(250000000)
print("pi:",pi)
print("tm:",time.time()-start)
总结到现在:
rand() 函数似乎消耗了大部分时间。使用像这样的确定性方法
...
ns = (long) sqrt((double)nsamples)+1;
dx = 1./sqrt((double)nsamples);
dy = dx;
...
for(i=0;i<ns;i++)
for(k=0;k<ns;k++)
{
x = i*dx;
y = k*dy;
if((x*x + y*y) < 1.0) { accLoc += 1;}
}
...
而不是 rand() 导致执行时间仅为 0.04 秒!显然 Numba 使用了另一个更高效的随机函数。
最近在Python做了一些性能优化方面的测试。其中一部分是使用 SWIG 对 Monte-Carlo Pi 计算进行基准测试,并编译一个库以导入 Python。另一个解决方案是使用 Numba。现在我完全想知道为什么原生 C 解决方案比 Numba 更糟糕,即使 LLVM 编译器都用于两者。所以我想知道我是否做错了什么。
在我的笔记本电脑上运行
native C module: 7.09 s
Python+Numba: 2.75 s
原生 C 代码
#include "swigtest.h"
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
float monte_carlo_pi(long nsamples)
{
int accGlob=0;
int accLoc=0;
int i,ns;
float x,y;
float res;
float iRMX=1.0/(float) RAND_MAX;
srand(time(NULL));
for(i=0;i<nsamples;i++)
{
x = (float)rand()*iRMX;
y = (float)rand()*iRMX;
if((x*x + y*y) < 1.0) { acc += 1;}
}
res = 4.0 * (float) acc / (float) nsamples;
printf("cres = %.5f\n",res);
return res;
}
swigtest.i
%module swigtest
%{
#define SWIG_FILE_WITH_INIT
#include "swigtest.h"
%}
float monte_carlo_pi(long nsamples);
编译器调用
clang.exe swigtest.c swigtest_wrap.c -Ofast -o _swigtest.pyd -I C:\python37\include -shared -L c:\python37\libs -g0 -mtune=intel -msse4.2 -mmmx
testswig.py
from swigtest import monte_carlo_pi
import time
import os
start = time.time()
pi = monte_carlo_pi(250000000)
print("pi: %.5f" % pi)
print("tm:",time.time()-start)
Python 带有 Numba 的版本
from numba import jit
import random
import time
start = time.time()
@jit(nopython=True,cache=True,fastmath=True)
def monte_carlo_pi(nsamples: int)-> float:
acc:int = 0
for i in range(nsamples):
x:float = random.random()
y:float = random.random()
if (x * x + y * y) < 1.0: acc += 1
return 4.0 * acc / nsamples
pi = monte_carlo_pi(250000000)
print("pi:",pi)
print("tm:",time.time()-start)
总结到现在:
rand() 函数似乎消耗了大部分时间。使用像这样的确定性方法
...
ns = (long) sqrt((double)nsamples)+1;
dx = 1./sqrt((double)nsamples);
dy = dx;
...
for(i=0;i<ns;i++)
for(k=0;k<ns;k++)
{
x = i*dx;
y = k*dy;
if((x*x + y*y) < 1.0) { accLoc += 1;}
}
...
而不是 rand() 导致执行时间仅为 0.04 秒!显然 Numba 使用了另一个更高效的随机函数。