在Python与Erlang中分析列表反转时出现的问题

在Python与Erlang中分析列表反转时出现的问题,python,list,erlang,profiling,implementation,Python,List,Erlang,Profiling,Implementation,我分析了Erlang的列表:反向内置函数BIF,以查看它随输入大小的伸缩性。更具体地说,我试着: 1> X=列表:序号1000000。 [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22, 23,24,25,26,27,28,29|...] 2> 计时器:tclists,反向[X]。 {57737, [1000000,999999,999998,999997,999996,999995,999994,999993, 999992,9

我分析了Erlang的列表:反向内置函数BIF,以查看它随输入大小的伸缩性。更具体地说,我试着:

1> X=列表:序号1000000。 [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22, 23,24,25,26,27,28,29|...] 2> 计时器:tclists,反向[X]。 {57737, [1000000,999999,999998,999997,999996,999995,999994,999993, 999992,999991,999990,999989,999988,999987,999986,999985, 999984,999983,999982,999981,999980,999979,999978,999977, 999976,999975,999974|...]} 3> 计时器:tclists,反向[X]。 {46896, [1000000,999999,999998,999997,999996,999995,999994,999993, 999992,999991,999990,999989,999988,999987,999986,999985, 999984,999983,999982,999981,999980,999979,999978,999977, 999976,999975,999974|...]} 4> Y=列表:序号10000000。 [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22, 23,24,25,26,27,28,29|...] 5> 计时器:tclists,反向[Y]。 {434079, [10000000,9999999,9999998,9999997,9999996,9999995,9999994, 9999993,9999992,9999991,9999990,9999989,9999988,9999987, 9999986,9999985,9999984,9999983,9999982,9999981,9999980, 9999979,9999978,9999977,9999976,9999975,9999974|...]} 6> 计时器:tclists,反向[Y]。 {214173, [10000000,9999999,9999998,9999997,9999996,9999995,9999994, 9999993,9999992,9999991,9999990,9999989,9999988,9999987, 9999986,9999985,9999984,9999983,9999982,9999981,9999980, 9999979,9999978,9999977,9999976,9999975,9999974|...]} 好的,到目前为止,似乎反向BIF在近似线性时间内对输入进行缩放,例如,将输入的大小乘以10,所用时间的大小也增加了10倍。在纯Erlang中,这是有意义的,因为我们将使用类似尾部递归的方法来反转列表。我猜想,即使是用C实现的BIF,反转列表的算法似乎是相同的,可能是因为列表在Erlang?中的表示方式不同

现在我想将它与另一种语言进行比较——也许是另一种我已经使用过的动态类型语言。因此,我在Python中尝试了类似的事情——非常明确地注意使用实际的列表,而不是生成器,我预计这将在本测试中对Python的性能产生积极的影响,给它带来不公平的优势

导入时间 ms_conv_系数=10**6 def profilefunc,*参数: 开始=时间 函数 结束=时间 已用秒数=结束-开始 打印经过的秒数*毫秒转换系数,刷新=真 x=列表[i代表范围为1000000的i] y=列表[i代表范围为10000000的i] z=列表[i代表范围为100000000的i] def fm: 返回m[:-1] def gm: 回程反向电火花加工 如果uuuu name uuuuu==\uuuuuuuu main\uuuuuuuu: printAll done加载列表,从现在开始,flush=True printf: 轮廓f,x 轮廓 打印 轮廓f,x 轮廓 打印 轮廓f,z 打印 打印: 剖析,x 剖析,y 打印 剖析,x 剖析,y 打印 轮廓,z 这似乎表明,在函数加载并运行一次后,输入的长度没有差别,反转时间非常快,在~0.7µs的范围内

确切结果:

All done loading the lists, starting now.
f:
1.430511474609375
0.7152557373046875

0.7152557373046875
0.2384185791015625

0.476837158203125

g:
1.9073486328125
0.7152557373046875

0.2384185791015625
0.2384185791015625

0.476837158203125
我的第一个天真的猜测是,python可能能够识别反向构造并创建类似于反向迭代器的东西,然后返回python可以使用引用的结果,对吗?也许它在这里使用了某种优化。但是我不认为这个理论有意义,因为原始列表和返回的列表是不同的,一个不应该改变另一个

所以我的问题是:

我的分析技术有缺陷吗?我写测试的方式是否有利于一种语言而不是另一种语言? 列表的实现和它们在Erlang和Python中的反转有什么不同,使得Python的速度更快? 提前谢谢你的时间

这似乎表明在加载并运行函数之后 一次,输入的长度与反转没有区别 时间非常快-在~0.7µs的范围内

因为您的分析功能不正确。它接受变量位置参数,但当它将它们传递给函数时,它不会解压缩它们,因此您只能使用长度为1的元组。您需要执行以下操作:

def profile(func, *args):
    start = time.time()
    func(*args) # Make sure to unpack the args!
    end = time.time()
    elapsed_seconds = end - start
    print(elapsed_seconds * ms_conv_factor, flush=True)
因此,请注意区别:

>>> def foo(*args):
...    print(args)
...    print(*args)
...
>>> foo(1,2,3)
(1, 2, 3)
1 2 3
还要注意的是,ReverseEDM创建了一个反向迭代器,所以在您对其进行迭代之前,它实际上不会做任何事情。所以g仍然是常数时间

但请放心,在Python中反转列表需要线性时间

这似乎表明在加载并运行函数之后 一次,输入的长度与反转没有区别 时间非常快-在~0.7µs的范围内

因为您的分析功能不正确。它接受变量位置参数,但当它将它们传递给函数时,它不接受 不要打开它们,因此您只能处理长度为1的元组。您需要执行以下操作:

def profile(func, *args):
    start = time.time()
    func(*args) # Make sure to unpack the args!
    end = time.time()
    elapsed_seconds = end - start
    print(elapsed_seconds * ms_conv_factor, flush=True)
因此,请注意区别:

>>> def foo(*args):
...    print(args)
...    print(*args)
...
>>> foo(1,2,3)
(1, 2, 3)
1 2 3
还要注意的是,ReverseEDM创建了一个反向迭代器,所以在您对其进行迭代之前,它实际上不会做任何事情。所以g仍然是常数时间


但请放心,在Python中反转列表需要线性时间。

注意,在得到相当大的列表之前,使用生成器方法实际上会比较慢。另外,诸如:x=list[i for i in range0,1000000]之类的表达式是多余的,列表理解已经返回列表。但实际上,[x for x in whatever]只是写listwhatever的一种冗长的方式,所以你只需要ListRange1000000注释,使用生成器方法实际上会比较慢,直到你得到相当大的列表。另外,像:x=list[i for i in range0,1000000]这样的表达式是多余的,列表理解已经返回了列表。但事实上,[x代表x]只是一种冗长的方式来写ListAnswerk,所以你只想要ListAnswerk,多么尴尬!这是我犯的一个相当愚蠢的错误——我应该花更多的时间查看分析代码。修正后,我得到一个线性趋势。这更有意义,尤其是你对我的评论。谢谢你的帮助!哎呀,多尴尬啊!这是我犯的一个相当愚蠢的错误——我应该花更多的时间查看分析代码。修正后,我得到一个线性趋势。这更有意义,尤其是你对我的评论。谢谢你的帮助!