为什么我的 Python 代码比 PHP 中的相同代码慢 100 倍?
Why is my Python code 100 times slower than the same code in PHP?
我有两个点(x1 和 x2)并且想要在给定的步数中生成正态分布。 x1和x2之间的x值的y值之和为1。到实际问题:
我是 Python 的新手,想知道为什么以下代码产生了预期的结果,但比 PHP 中的相同程序慢了大约 100 倍。大约有 2000 个 x1-x2 对,每对大约有 5 个步长值。
我尝试使用 Cython 进行编译,使用了多处理,但它只改进了 2 倍,仍然比 PHP 慢 50 倍。关于如何提高速度以匹配至少 PHP 性能的任何建议?
from scipy.stats import norm
import numpy as np
import time
# Calculates normal distribution
def calculate_dist(x1, x2, steps, slope):
points = []
range = np.linspace(x1, x2, steps+2)
for x in range:
y = norm.pdf(x, x1+((x2-x1)/2), slope)
points.append([x, y])
sum = np.array(points).sum(axis=0)[1]
norm_points = []
for point in points:
norm_points.append([point[0], point[1]/sum])
return norm_points
start = time.time()
for i in range(0, 2000):
for j in range(10, 15):
calculate_dist(0, 1, j, 0.15)
print(time.time() - start) # Around 15 seconds or so
编辑,PHP代码:
$start = microtime(true);
for ($i = 0; $i<2000; $i++) {
for ($j = 10; $j<15; $j++) {
$x1 = 0; $x2 = 1; $steps = $j; $slope = 0.15;
$step = abs($x2-$x1) / ($steps + 1);
$points = [];
for ($x = $x1; $x <= $x2 + 0.000001; $x += $step) {
$y = stats_dens_normal($x, $x1 + (($x2 - $x1) / 2), $slope);
$points[] = [$x, $y];
}
$sum = 0;
foreach ($points as $point) {
$sum += $point[1];
}
$norm_points = [];
foreach ($points as &$point) {
array_push($norm_points, [$point[0], $point[1] / $sum]);
}
}
}
return microtime(true) - $start; # Around 0.1 seconds or so
编辑2,剖析每一行,发现norm.pdf()占用了98%的时间,于是找到了一个自定义的normpdf函数并定义了它,现在时间大约是0.67s,速度相当快,但是仍然比 PHP 慢 10 倍左右。另外我认为重新定义通用函数违背了 Python 简单性的想法?!
自定义函数(来源是其他一些 Whosebug 答案):
from math import sqrt, pi, exp
def normpdf(x, mu, sigma):
u = (x-mu)/abs(sigma)
y = (1/(sqrt(2*pi)*abs(sigma)))*exp(-u*u/2)
return y
答案是,您没有为 python 中的任务使用正确的 tools/data 结构。
在 python 中调用 numpy 功能有相当大的开销(scipy.stats.norm.pdf
在后台使用 numpy),因此永远不会为一个元素调用此函数,而是为整个数组(所谓的矢量化)计算),这意味着而不是
for x in range:
y = norm.pdf(x, x1+((x2-x1)/2), slope)
ys.append(y)
有人宁愿使用:
ys = norm.pdf(x,x1+((x2-x1)/2), slope)
计算 x 中所有元素的 pdf 并且只支付一次开销而不是 len(x)
次。
例如,计算 10^4 个元素的 pdf 所花费的时间比计算一个元素的时间多不到 10 倍:
%timeit norm.pdf(0) # 68.4 µs ± 1.62 µs
%timeit norm.pdf(np.zeros(10**4)) # 415 µs ± 12.4 µs
使用向量化计算不仅可以让你的程序更快,而且通常shorter/easier更容易理解,例如:
def calculate_dist_vec(x1, x2, steps, slope):
x = np.linspace(x1, x2, steps+2)
y = norm.pdf(x, x1+((x2-x1)/2), slope)
ys = y/np.sum(y)
return x,ys
使用此矢量化版本可将速度提高 10 左右。
问题:norm.pdf
针对长向量进行了优化(没有人真正关心 fast/slow 如果它对 100 万个元素非常快,它对 10 个元素的优化如何),但是你的测试有偏见numpy,因为它 uses/creates 只有短数组,因此 norm.pdf
不能发光。
因此,如果它真的是关于小数组并且你真的想加快它的速度,你将不得不推出你自己的版本 norm.pdf
使用 cython 创建这个快速而专业的函数可能值得一试.
我有两个点(x1 和 x2)并且想要在给定的步数中生成正态分布。 x1和x2之间的x值的y值之和为1。到实际问题:
我是 Python 的新手,想知道为什么以下代码产生了预期的结果,但比 PHP 中的相同程序慢了大约 100 倍。大约有 2000 个 x1-x2 对,每对大约有 5 个步长值。
我尝试使用 Cython 进行编译,使用了多处理,但它只改进了 2 倍,仍然比 PHP 慢 50 倍。关于如何提高速度以匹配至少 PHP 性能的任何建议?
from scipy.stats import norm
import numpy as np
import time
# Calculates normal distribution
def calculate_dist(x1, x2, steps, slope):
points = []
range = np.linspace(x1, x2, steps+2)
for x in range:
y = norm.pdf(x, x1+((x2-x1)/2), slope)
points.append([x, y])
sum = np.array(points).sum(axis=0)[1]
norm_points = []
for point in points:
norm_points.append([point[0], point[1]/sum])
return norm_points
start = time.time()
for i in range(0, 2000):
for j in range(10, 15):
calculate_dist(0, 1, j, 0.15)
print(time.time() - start) # Around 15 seconds or so
编辑,PHP代码:
$start = microtime(true);
for ($i = 0; $i<2000; $i++) {
for ($j = 10; $j<15; $j++) {
$x1 = 0; $x2 = 1; $steps = $j; $slope = 0.15;
$step = abs($x2-$x1) / ($steps + 1);
$points = [];
for ($x = $x1; $x <= $x2 + 0.000001; $x += $step) {
$y = stats_dens_normal($x, $x1 + (($x2 - $x1) / 2), $slope);
$points[] = [$x, $y];
}
$sum = 0;
foreach ($points as $point) {
$sum += $point[1];
}
$norm_points = [];
foreach ($points as &$point) {
array_push($norm_points, [$point[0], $point[1] / $sum]);
}
}
}
return microtime(true) - $start; # Around 0.1 seconds or so
编辑2,剖析每一行,发现norm.pdf()占用了98%的时间,于是找到了一个自定义的normpdf函数并定义了它,现在时间大约是0.67s,速度相当快,但是仍然比 PHP 慢 10 倍左右。另外我认为重新定义通用函数违背了 Python 简单性的想法?!
自定义函数(来源是其他一些 Whosebug 答案):
from math import sqrt, pi, exp
def normpdf(x, mu, sigma):
u = (x-mu)/abs(sigma)
y = (1/(sqrt(2*pi)*abs(sigma)))*exp(-u*u/2)
return y
答案是,您没有为 python 中的任务使用正确的 tools/data 结构。
在 python 中调用 numpy 功能有相当大的开销(scipy.stats.norm.pdf
在后台使用 numpy),因此永远不会为一个元素调用此函数,而是为整个数组(所谓的矢量化)计算),这意味着而不是
for x in range:
y = norm.pdf(x, x1+((x2-x1)/2), slope)
ys.append(y)
有人宁愿使用:
ys = norm.pdf(x,x1+((x2-x1)/2), slope)
计算 x 中所有元素的 pdf 并且只支付一次开销而不是 len(x)
次。
例如,计算 10^4 个元素的 pdf 所花费的时间比计算一个元素的时间多不到 10 倍:
%timeit norm.pdf(0) # 68.4 µs ± 1.62 µs
%timeit norm.pdf(np.zeros(10**4)) # 415 µs ± 12.4 µs
使用向量化计算不仅可以让你的程序更快,而且通常shorter/easier更容易理解,例如:
def calculate_dist_vec(x1, x2, steps, slope):
x = np.linspace(x1, x2, steps+2)
y = norm.pdf(x, x1+((x2-x1)/2), slope)
ys = y/np.sum(y)
return x,ys
使用此矢量化版本可将速度提高 10 左右。
问题:norm.pdf
针对长向量进行了优化(没有人真正关心 fast/slow 如果它对 100 万个元素非常快,它对 10 个元素的优化如何),但是你的测试有偏见numpy,因为它 uses/creates 只有短数组,因此 norm.pdf
不能发光。
因此,如果它真的是关于小数组并且你真的想加快它的速度,你将不得不推出你自己的版本 norm.pdf
使用 cython 创建这个快速而专业的函数可能值得一试.