我跑到命令
下面MIX_ENV=prod mix profile.fprof --no-start -e "Math.prime_seq 501"
以下代码
def prime_seq(n) do
prime_seq(n, 1, 3, [2,3,5,7,11,13,17,19,23])
end
def prime_seq(n, c, p, cache) when c < n do
is_it = cache |> Enum.any?(fn n -> rem(p, n) == 0 end)
if not is_it do
prime_seq(n, c+1, p+2, cache ++ [p])
else
if(is_prime(p)) do
prime_seq(n, c+1, p+2, cache ++ [p])
else
prime_seq(n, c, p+2, cache)
end
end
end
def prime_seq(n, c, p, _) when c == n do
p-2
end
结果:
为什么Enum.do_any?
需要花费太多时间?
是的,这是一个愚蠢的算法来查找第n个素数,并且有更好的算法。但重点是,使Enum.any?
比使用专门的函数迭代列表要慢。
我相信anom func是rem(p,n)
,CMIIW
更新
我删除了Enum.any?
,名为divisible?
def divisible?(n, [h|t]) do
if rem(n, h) == 0 do
true
else
divisible?(n, t)
end
end
def divisible?(n, []) do
false
end
.....
def prime_seq(n, c, p, cache) when c < n do
#is_it = cache |> Enum.any?(fn n -> rem(p, n) == 0 end)
is_it = divisible?(p, cache)
if not is_it do
prime_seq(n, c+1, p+2, cache ++ [p])
else
if(is_prime(p)) do
prime_seq(n, c+1, p+2, cache ++ [p])
else
prime_seq(n, c, p+2, cache)
end
end
end
.....
结果:
所以..通过简单的修改,我可以使它快3倍,迭代次数也相同。
注意:在学习灵丹妙药时,这是一个玩具项目。请耐心等待。
is_prime函数:
def is_prime(n, i) when i < n do
if rem(n, i) == 0 do
false
else
is_prime(n, i+1)
end
end
def is_prime(n, i) when i >= n do
true
end
def is_prime(n) do
cond do
n <= 1 -> false
n <= 3 -> true
rem(n, 2) == 0 or rem(n, 3) == 0 -> false
true -> is_prime(n, 3)
end
end
答案 0 :(得分:5)
因为我被问过,所以我会把它作为答案而不是评论。
问题不在于匿名函数是“昂贵的”,而是代码几乎没有做任何事情,只是在列表上进行迭代。
BEAM调度程序使用缩减来执行功能切片。这有点复杂,但每个函数调用都算作一次减少。当您使用匿名函数时,您正在增加缩减的时间成本(即,将实际函数的查找添加到时间成本中 减少。)通常这个额外的成本可以忽略不计,但是当你这样做时 数百万次,它加起来。
BEAM调度程序为每个进程2000减少,然后为时间片 一个新的过程。
您创建了一个病态边缘案例,将零值与另一个值进行比较。如果您将零与任何看起来“昂贵”的东西进行比较,那么无论绝对比例的值有多大或多小都无关紧要。
正确的结论是,比O(n)缩放得更快的递归算法对每次递归中完成的工作量非常敏感。你应该感到惊讶的是,这根本不起作用,而不是它很慢。
如果我今天有时间,我会尝试使用以下各种情况获得一些减少计数:erlang.statistics(:exact_reductions)。
我使用此代码来获取一些基本指标。
defmodule Counter do
def count(function,arg) do
{_ , count } = :erlang.process_info(self,:reductions)
function.(arg)
{_ , new_count } = :erlang.process_info(self,:reductions)
new_count - count
end
end
这个代码计算减少的方式并不完美,它应该真的在它的运行 自己的过程。
这是我得到的结果,首先是Enum.any?版本
iex(4)> Counter.count(&Math.prime_seq/1, 10)
283
iex(5)> Counter.count(&Math.prime_seq/1, 100)
14086
iex(6)> Counter.count(&Math.prime_seq/1, 1000)
1105114
iex(7)> Counter.count(&Math.prime_seq/1, 10000)
103654258
iex(8)> Counter.count(&Math.prime_seq/1, 100000)
10068833898
请注意,所有这些减少都发生在一个调度线程中,我的8核笔记本电脑在此测试期间几乎没有忙。很明显,这是一种减少O(n ** 2)算法。现在有了可分割的功能
iex(1)> Counter.count(&Math.prime_seq_div/1, 10)
283
iex(2)> Counter.count(&Math.prime_seq_div/1, 100)
14062
iex(3)> Counter.count(&Math.prime_seq_div/1, 1000)
1105485
iex(4)> Counter.count(&Math.prime_seq_div/1, 10000)
103655170
iex(5)> Counter.count(&Math.prime_seq_div/1, 100000)
10068870615
老实说,我预计这些数字会更小。事实上,他们并没有让我得出关于正在发生的事情的另一个结论,这不是减少,而是减少更多的时间。
答案 1 :(得分:1)
查看source code of Enum.do_any?
,我们可以看到它所做的就是遍历列表并调用提供的lambda。
分析结果似乎表明大部分时间都花在lambda之外,即在迭代时,这在某种程度上让我困惑。
无论如何,对这些结果的解释是大部分时间花在这一行上:
is_it = cache |> Enum.any?(fn n -> rem(p, n) == 0 end)
另一个有用的信息是代码对输入大小为501进行了135k次迭代。这是一个非常好的指示,算法复杂度至少是多项式。
基于此,我建议考虑一些算法改变,例如Eratosthenes的筛子。不幸的是,我无法弄清楚这个代码假设要返回什么,所以我无法提供替代解决方案。
答案 2 :(得分:0)
如果您需要极端性能,Elixir / Erlang可能不是正确的工具。 Erlang VM性能不适合CPU绑定任务。相反,您可能需要调用高度优化的外部库,例如primesieve。您可以学习如何编写所谓的端口,以便在this recent Elixir Sips episode(免费)中调用本机代码。
答案 3 :(得分:0)
要回答为什么divisible?
慢于cache |> Enum.any?(fn n -> rem(p, n) == 0 end)
的问题我为非分析版本创建了一个简单的测试用例:
divisible?(p, cache)
divisible_anon?(cache, fn n -> rem(p, n) == 0 end)
def divisible_anon?([h|t], fun) do
if fun.(h) do
true
else
divisible_anon?(t, fun)
end
end
def divisible_anon?([], _) do
false
end
def divisible?(n, [h|t]) do
if rem(n, h) == 0 do
true
else
divisible?(n, t)
end
end
def divisible?(n, []) do
false
end
测试代码是:
def measure(function) do
function
|> :timer.tc
|> elem(0)
|> Kernel./(1_000_000)
end
测量:
Math.prime_seq 501
Math.prime_seq 10001
的结果:
{{1}}的结果:
结论,在@sasajuric的帮助下:&#34;匿名函数调用很昂贵&#34;
答案 4 :(得分:0)
Enum.any?
是通过依赖Enumerable(使用reducees)来实现的:http://blog.plataformatec.com.br/2015/05/introducing-reducees/
泛化与专业化相反。因此,虽然我们可以使用任何数据类型,但它不会像您自己实现列表变体一样快。但是,这并不意味着我们必然会变慢,我们可以轻松地在Enum中内联这些操作,从而降低成本。