状态:到目前为止,最佳答案的程序在原始程序的33%的时间内执行!但可能还有其他方法可以优化它。
Lua目前是最快的脚本语言,但Lua在针对C / C ++的几个基准测试中得分非常糟糕。
其中一个是mandelbrot测试(Generate Mandelbrot设置便携式位图文件N = 16,000),其中得分可怕1:109(多核)或1:28(单核)
由于Delta的速度非常大,因此这是优化的理想选择。此外,我确信那些知道Mike Pall是谁的人可能认为不可能进一步优化这一点,但这显然是错误的。任何做过优化的人都知道总是可以做得更好。此外,我通过一些调整设法获得了一些额外的性能,所以我知道它可能:)
-- The Computer Language Shootout
-- http://shootout.alioth.debian.org/
-- contributed by Mike Pall
local width = tonumber(arg and arg[1]) or 100
local height, wscale = width, 2/width
local m, limit2 = 50, 4.0
local write, char = io.write, string.char
write("P4\n", width, " ", height, "\n")
for y=0,height-1 do
local Ci = 2*y / height - 1
for xb=0,width-1,8 do
local bits = 0
local xbb = xb+7
for x=xb,xbb < width and xbb or width-1 do
bits = bits + bits
local Zr, Zi, Zrq, Ziq = 0.0, 0.0, 0.0, 0.0
local Cr = x * wscale - 1.5
for i=1,m do
local Zri = Zr*Zi
Zr = Zrq - Ziq + Cr
Zi = Zri + Zri + Ci
Zrq = Zr*Zr
Ziq = Zi*Zi
if Zrq + Ziq > limit2 then
bits = bits + 1
break
end
end
end
if xbb >= width then
for x=width,xbb do bits = bits + bits + 1 end
end
write(char(255-bits))
end
end
那么如何对其进行优化(当然,与任何优化一样,您必须测量实施以确保更快)。并且你不允许为此改变Lua的C核心,或者使用LuaJit,它可以找到优化Lua弱点之一的方法。
修改:在此基础上获得奖励,使挑战更有趣。
答案 0 :(得分:7)
通过2,比我以前更好(在我的机器上)约30%。主要的节省来自展开内部循环以分摊开销。
还包括(已注释掉)是当你陷入中心心形时,通过提前退出(并将像素设置为黑色)来节省时间的中止尝试。它有效,但无论我如何摇晃它都会慢一些。
我必须跑步,但我会留下一个离别的建议。可能通过对结果进行行程编码来进行一些优化(因此,不是保存一堆bit-twiddled字符,而是保存列表(白点数,黑点数,白点数等)。 )。这会:
不知道它是否可以被编码得足够紧密以便飞行,但如果我有更多时间的话,我会尝试下一步。
-- The Computer Language Shootout
-- http://shootout.alioth.debian.org/
-- contributed by Mike Pall
-- with optimizations by Markus J. Q. (MarkusQ) Roberts
local width = tonumber(arg and arg[1]) or 100
local height, wscale = width, 2/width
local m, limit2 = 50, 4.0
local write, char = io.write, string.char
local h2 = math.floor(height/2)
local hm = height - h2*2
local top_half = {}
for y=0,h2+hm do
local Ci = 2*y / height - 1
local line = {""}
for xb=0,width-1,8 do
local bits = 0
local xbb = xb+7
for x=xb,xbb < width and xbb or width-1 do
bits = bits + bits
local Zr, Zi, Zrq, Ziq = 0.0, 0.0, 0.0, 0.0
local Cr = x * wscale - 1.5
local Zri = Zr*Zi
for i=1,m/5 do
Zr = Zrq - Ziq + Cr
Zi = Zri + Zri + Ci
Zri = Zr*Zi
Zr = Zr*Zr - Zi*Zi + Cr
Zi = 2*Zri + Ci
Zri = Zr*Zi
Zr = Zr*Zr - Zi*Zi + Cr
Zi = 2*Zri + Ci
Zri = Zr*Zi
Zr = Zr*Zr - Zi*Zi + Cr
Zi = 2*Zri + Ci
Zri = Zr*Zi
Zr = Zr*Zr - Zi*Zi + Cr
Zi = 2*Zri + Ci
Zri = Zr*Zi
Zrq = Zr*Zr
Ziq = Zi*Zi
Zri = Zr*Zi
if Zrq + Ziq > limit2 then
bits = bits + 1
break
end
-- if i == 1 then
-- local ar,ai = 1-4*Zr,-4*Zi
-- local a_r = math.sqrt(ar*ar+ai*ai)
-- local k = math.sqrt(2)/2
-- local br,bi2 = math.sqrt(a_r+ar)*k,(a_r-ar)/2
-- if (br+1)*(br+1) + bi2 < 1 then
-- break
-- end
-- end
end
end
for x=width,xbb do
bits = bits + bits + 1
end
table.insert(line,char(255-bits))
end
line = table.concat(line)
table.insert(top_half,line)
end
write("P4\n", width, " ", height, "\n")
for y=1,h2+hm do
write(top_half[y])
end
for y=h2,1,-1 do
write(top_half[y])
end
答案 1 :(得分:3)
所以这里约为40%:
-- The Computer Language Shootout
-- http://shootout.alioth.debian.org/
-- contributed by Mike Pall
local width = tonumber(arg and arg[1]) or 100
local height, wscale = width, 2/width
local m, limit2 = 50, 4.0
local write, char = io.write, string.char
function addChar (line, c)
table.insert(line, c)
for i=table.getn(line)-1, 1, -1 do
if string.len(line[i]) > string.len(line[i+1]) then
break
end
line[i] = line[i] .. table.remove(line)
end
end
local h2 = math.floor(height/2)
local hm = height - h2*2
local top_half = {}
for y=0,h2+hm do
local Ci = 2*y / height - 1
local line = {""}
for xb=0,width-1,8 do
local bits = 0
local xbb = xb+7
for x=xb,xbb < width and xbb or width-1 do
bits = bits + bits
local Zr, Zi, Zrq, Ziq = 0.0, 0.0, 0.0, 0.0
local Cr = x * wscale - 1.5
for i=1,m do
local Zri = Zr*Zi
Zr = Zrq - Ziq + Cr
Zi = Zri + Zri + Ci
Zrq = Zr*Zr
Ziq = Zi*Zi
if Zrq + Ziq > limit2 then
bits = bits + 1
break
end
end
end
for x=width,xbb do
bits = bits + bits + 1
end
addChar(line,char(255-bits))
end
line = table.concat(line)
table.insert(top_half,line)
end
write("P4\n", width, " ", height, "\n")
for y=1,h2+hm do
write(top_half[y])
end
for y=h2,1,-1 do
write(top_half[y])
end
- MarkusQ
答案 2 :(得分:2)
现在至少有一个答案比我的解决方案快,我会发布我的答案
-- The Computer Language Shootout
-- http://shootout.alioth.debian.org/
-- contributed by Mike Pall
local width = tonumber(arg and arg[1]) or 100
local height, wscale = width, 2/width
local m, limit2 = 50, 4.0
local write, char = io.write, string.char
local insert = table.insert
local results={}
write("P4\n", width, " ", height, "\n")
for y=0,height-1 do
local Ci = 2*y / height - 1
for xb=0,width-1,8 do
local bits = 0
local xbb = xb+7
for x=xb,xbb < width and xbb or width-1 do
bits = bits + bits
local Zr, Zi, Zrq, Ziq = 0.0, 0.0, 0.0, 0.0
local Cr = x * wscale - 1.5
for i=1,m do
local Zri = Zr*Zi
Zr = Zrq - Ziq + Cr
Zi = Zri + Zri + Ci
Zrq = Zr*Zr
Ziq = Zi*Zi
if Zrq + Ziq > limit2 then
bits = bits + 1
break
end
end
end
if xbb >= width then
for x=width,xbb do bits = bits + bits + 1 end
end
insert(results,(char(255-bits)))
end
end
write(table.concat(results))
技巧是在将值发送到输出之前将值存储到表中。 像这样简单的东西将运行时间减少到58%。
答案 3 :(得分:2)
我不知道Lua生产工作代码是好事,但你应该能够通过使用一些数学技巧来大幅增加Mandelbrot的性能。有人建议使用对称来加速它,使用这种优化可以做另一项重大改进:
使用使用Mandelbrot部分的矩形坐标的递归函数。然后计算矩形边界线处的值和中间分割的两条线。在此之后,有4个子矩形。如果其中一个具有相同的边框像素颜色,则可以使用此颜色填充它,如果没有,则递归调用此部分的函数。
我搜索了这个算法的另一个解释,发现了一个here - 以及一个不错的visualization。旧的DOS程序FRACTINT调用此optimization“Tesseral模式”。
答案 4 :(得分:1)
答案 5 :(得分:1)
为什么要使用局部变量Zri?可以通过重新排序下两个语句来避免使用它:
Zi = 2 * Zr * Zi + Ci Zr = Zrq-Ziq + Cr
也可以使用简单的周期性检查,但加速取决于m。越大的“m”越大,从周期性检查中获得的加速就越好。
答案 6 :(得分:0)
当你引用基准游戏中的数字时,请显示这些数字的来源,以便读者有一些背景。
在这种情况下,您似乎已经在四核计算机上测量了数字,其中最快的程序已被重写以利用多个核心。而不是查看经过的时间sort by CPU time and you'll see the ratio drop to 1:28。
或者查看中位数和四分位数以获得对how the set of C++ measurements compares to the set of Lua measurements的更好印象。
或者有一整套测量,程序被迫只使用一个核心 - Lua compared with C++ - 如果你看一下those Lua pi-digits programs,你会发现他们使用的是C语言GNU GMP库。
答案 7 :(得分:0)
我做的下一步是缓存一遍又一遍计算的东西,并将bit + bit替换为bit * 2,这些简单的优化非常强大......
local width = tonumber(arg and arg[1]) or 100
local height, wscale = width, 2/width
local m, limit2 = 50, 4.0
local write, char = io.write, string.char
local results={}
write("P4\n", width, " ", height, "\n")
local height_minus_one = height - 1
local width_minus_one = width -1
for y=0,height_minus_one do
local Ci = 2*y / height_minus_one
for xb=0,width_minus_one,8 do
local bits = 0
local xbb = xb+7
for x=xb,xbb < width and xbb or width_minus_one do
bits = bits *2
local Zr, Zi, Zrq, Ziq = 0.0, 0.0, 0.0, 0.0
local Cr = x * wscale - 1.5
for i=1,m do
local Zri = Zr*Zi
Zr = Zrq - Ziq + Cr
Zi = Zri + Zri + Ci
Zrq = Zr*Zr
Ziq = Zi*Zi
if Zrq + Ziq > limit2 then
bits = bits + 1
break
end
end
end
if xbb >= width then
for x=width,xbb do bits = bits *2 + 1 end
end
table.insert(results,(char(255-bits)))
end
end
write(table.concat(results))
这种优化使得程序在原始时间的34%的时间内运行,但Markus Q的优化仍然优于我的;)
答案 8 :(得分:0)
这是另一次尝试,但事实证明它比变量的本地访问慢(我想象使用干净的环境会使查找变量更快,但事实并非如此,本地的虚拟寄存器稍微有点更快)这使运行时间降至41%。
local env={}
env.width = tonumber(arg and arg[1]) or 100
env.height = env.width
env.wscale = 2/env.width
env.m = 50
env.limit2 = 4.0
env.write = io.write
env.char = string.char
env.results={}
env.height_minus_one = env.height - 1
env.width_minus_one = env.width -1
env.insert = table.insert
setfenv(function()
write("P4\n", env.width, " ", env.height, "\n")
for y=0,height_minus_one do
local Ci = 2*y / height_minus_one
for xb=0,width_minus_one,8 do
local bits = 0
local xbb = xb+7
for x=xb,xbb < width and xbb or width_minus_one do
bits = bits *2
local Zr, Zi, Zrq, Ziq = 0.0, 0.0, 0.0, 0.0
local Cr = x * wscale - 1.5
for i=1,m do
local Zri = Zr*Zi
Zr = Zrq - Ziq + Cr
Zi = Zri + Zri + Ci
Zrq = Zr*Zr
Ziq = Zi*Zi
if Zrq + Ziq > limit2 then
bits = bits + 1
break
end
end
end
if xbb >= width then
for x=width,xbb do bits = bits *2 + 1 end
end
insert(results,(char(255-bits)))
end
end
end,env)()
io.write(table.concat(env.results))