分享

使Python代码的速度提高1000倍

 常有理 2020-01-01

如何比较两种候选解决方案的性能?

入门Python其实很容易,但是我们要去坚持学习,每一天坚持很困难,我相信很多人学了一个星期就放弃了,为什么呢?其实没有好的学习资料给你去学习,你们是很难坚持的,这是小编收集的Python入门学习资料关注,转发,私信小编“01”,即可免费领取!希望对你们有帮助

使Python代码的速度提高1000倍

性能可以参考解决方案中的许多不同因素(例如执行时间、CPU使用情况、内存使用情况等)。不过,在这篇文章中,我们将重点关注执行时间。

新解决方案的执行时间的改进可以简单地计算为进行除法。也就是说,我们将把旧的(或非优化的)解决方案的执行时间除以新的(或优化的)解决方案:TEST/tNew。这个指标通常被称为加速比...例如,如果我们的加速比因子为2,那么我们改进的解决方案所需的时间将是原解决方案的一半。

为了比较我们的函数的性能,我们将创建一个接收这两个函数的函数,计算它们的执行时间,并计算获得的加速比:

import timedef compute_speedup(slow_func, opt_func, func_name, tp=None): x = range(int(1e5)) if tp: x = list(map(tp, x)) slow_start = time.time() slow_func(x) slow_end = time.time() slow_time = slow_end - slow_start opt_start = time.time() opt_func(x) opt_end = time.time() opt_time = opt_end - opt_start speedup = slow_time/opt_time print('{} speedup: {}'.format(func_name, speedup))

为了获得重要的结果,我们将使用一个相对较大的数组(100000元素),并将其作为参数传递给这两个函数。然后,我们将使用时间模块计算执行时间,并最终交付所获得的加速比。

注意,我们还传递了一个可选参数,允许我们更改List元素的类型。

1.使用地图功能

当我们需要对列表中的每个元素进行操作时,我们通常可以这样做:我们使用列表理解遍历列表,并对当前元素进行工作:

def slow_map(x):  [str(n) for n in x]

然而,在许多情况下,您可能更喜欢使用Python内置映射函数,它将相同的操作应用于列表中的每个元素。最重要的是,可以简单地按以下方式实施:

def opt_map(x): map(str, x)

是时候检查一下我们已经改进了多少执行时间!运行计算加速比函数如下:

compute_speedup(slow_map, opt_map, 'map')

我得到了1099的加速比。这意味着,如果我们有一个使用map函数运行1秒的数组,那么使用列表理解运行大约需要18分钟!显然,如果我们将其与for循环实现进行比较,速度会更高。既然您有了验证它的工具,我将把它作为一个小练习留给您!

2.避免重新评价职能

每当您发现自己对循环块中的元素重复使用相同的函数时,例如:

y = []for n in x: y.append(n) y.append(n**2) y.append(n**3)

…或者只在循环块中使用这种函数一次,但是在一个大列表上使用,如下所示:

def slow_loop(x):  y = []  for n in x:    y.append(n) = []

…您可以利用另一种优化技术。这个场景假设您不能使用map(如果可以的话,可以使用它:加速比要高得多)。

如果以前将函数作为变量保存并在循环块中重用,则可以节省重新评估函数的成本。下面的片段显示了这种行为:

def opt_loop(x): y = [] append = y.append for n in x: append(n)

注意,如果需要将当前元素追加到不同的列表中,则必须为每个列表的append函数创建一个新变量。

让我们使用计算加速比检查加速比:

compute_speedup(slow_loop, opt_loop, 'loop')

在这种情况下,我得到了2.07的加速比!考虑到实现这一技术所需的工作量不多,我认为这并不算太糟。

3.避免将字符串与+运算符连接起来

您可能发现的一个常见情况是,必须形成一个具有多个子部分的字符串。Python有一个方便的+操作符,它允许我们以以下方式轻松地连接字符串:

def slow_join(x): s = '' for n in x: s += n

尽管对我们来说它似乎是一种干净的方法,但是Python的字符串被创建为不可变,因此不能被修改。这意味着每次我们使用+运算符时,Python实际上都是基于子字符串和返回新字符串创建一个新字符串。考虑一下,在我们的例子中,这个操作将被执行100000次。

这种方法显然是有代价的,我们可能会找到一种更便宜的解决方案加入(),如以下示例所示:

def opt_join(x):  s = ''.join(x)

此解决方案接受子字符串数组,并将它们与空字符串分隔符连接起来。让我们检查一下我们的性能改进:

compute_speedup(slow_join, opt_join, 'join', tp=str)

我的加速比是7.25!再说一遍,我们不需要做任何重大的改变来获得这样的改进。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多