为什么这个 Lua 优化技巧会提高性能? [英] Why would this Lua optimization hack improve performance?

查看:21
本文介绍了为什么这个 Lua 优化技巧会提高性能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在查看文档,该文档描述了提高 Lua 性能的各种技术 脚本代码,我很震惊需要这样的技巧.(虽然我引用了 Lua,但我在 Javascript 中看到过类似的 hack).

为什么需要这种优化:

<块引用>

例如代码

for i = 1, 1000000 做本地 x = math.sin(i)结尾

运行速度比这个慢 30%:

local sin = math.sin对于 i = 1, 1000000 做局部 x = sin(i)结尾

他们在本地重新声明了 sin 函数.

为什么这会有帮助?无论如何,这是编译器的工作.为什么程序员必须做编译器的工作?

我在 Javascript 中看到过类似的东西;所以很明显,解释编译器没有完成它的工作肯定有一个非常很好的理由.它是什么?

<小时>

我在我摆弄的 Lua 环境中反复看到它;人们将变量重新声明为本地变量:

local strfind = strfind本地 strlen = strlen本地 gsub = gsub本地对 = 对本地 ipairs = ipairs本地类型 = 类型本地 tinsert = tinsert本地颤音 = 颤音本地解包 = 解包局部最大值 = 最大值本地最小值 = 最小值当地楼层 = 楼层本地单元格 = 单元格本地负载字符串 = 负载字符串本地 tostring = tostring本地 setmetatable = setmetatable本地 getmetatable = getmetatable本地格式 = 格式局部罪 = math.sin

这里发生了什么,人们必须做编译器的工作?编译器是否对如何查找 format 感到困惑?为什么这是程序员必须处理的问题?为什么在 1993 年没有解决这个问题?

<小时>

我似乎也遇到了一个逻辑悖论:

  1. 优化不应该在没有分析的情况下进行
  2. Lua 没有被分析的能力
  3. Lua 不应该被优化

解决方案

为什么这会有帮助?无论如何,这是编译器的工作.为什么程序员必须做编译器的工作?

Lua 是一种动态语言.编译器可以在静态语言中进行很多推理,例如将常量表达式从循环中拉出.在动态语言中,情况有点不同.

Lua 的主要(也是唯一的)数据结构是表.math 也只是一个表,尽管它在这里用作命名空间.没有人可以阻止您修改循环中某处的 math.sin 函数(甚至认为这样做是不明智的),并且编译器在编译代码时无法知道这一点.因此,编译器完全按照您的指示执行:在循环的每次迭代中,在 math 表中查找 sin 函数并调用它.

现在,如果您知道您不会修改 math.sin(即您将调用相同的函数),您可以将其保存在循环外的局部变量中.因为没有查表,所以生成的代码速度更快.

LuaJIT 的情况有点不同 - 它使用跟踪和一些高级魔法来查看您的代码在运行时正在做什么,因此它实际上可以通过将表达式移到外部来优化循环循环和其他优化,除了实际将其编译为机器代码之外,使其变得非常快.

关于将变量重新声明为本地变量"——很多时候在定义模块时,您希望使用原始函数.当访问 pairsmax 或任何使用它们的全局变量的东西时,没有人可以向你保证每次调用都是相同的函数.例如 stdlib 重新定义了很多全局函数.

通过创建一个与全局同名的局部变量,你基本上将函数存储到一个局部变量中,并且因为局部变量(它们是词法范围的,这意味着它们在当前范围和任何嵌套范围中都是可见的)优先于全局变量,请确保始终调用相同的函数.如果稍后有人修改全局,它不会影响您的模块.更不用说它也更快了,因为全局变量是在全局表 (_G) 中查找的.

更新:我刚刚阅读了 Lua Performance TipsRoberto Ierusalimschy 是 Lua 的作者之一,它几乎解释了您需要了解的有关 Lua、性能和优化的所有信息.IMO 最重要的规则是:

<块引用>

规则 #1:不要这样做.

规则 #2:先不要这样做.(仅限专家)

I'm looking over a document that describes various techniques to improve performance of Lua script code, and I'm shocked that such tricks would be required. (Although I'm quoting Lua, I've seen similar hacks in Javascript).

Why would this optimization be required:

For instance, the code

for i = 1, 1000000 do 
   local x = math.sin(i) 
end

runs 30% slower than this one:

local sin = math.sin 
for i = 1, 1000000 do
    local x = sin(i) 
end

They're re-declaring sin function locally.

Why would this be helpful? It's the job of the compiler to do that anyway. Why is the programmer having to do the compiler's job?

I've seen similar things in Javascript; and so obviously there must be a very good reason why the interpreting compiler isn't doing its job. What is it?


I see it repeatedly in the Lua environment I'm fiddling in; people redeclaring variables as local:

local strfind = strfind
local strlen = strlen
local gsub = gsub
local pairs = pairs
local ipairs = ipairs
local type = type
local tinsert = tinsert
local tremove = tremove
local unpack = unpack
local max = max
local min = min
local floor = floor
local ceil = ceil
local loadstring = loadstring
local tostring = tostring
local setmetatable = setmetatable
local getmetatable = getmetatable
local format = format
local sin = math.sin

What is going on here that people have to do the work of the compiler? Is the compiler confused by how to find format? Why is this an issue that a programmer has to deal with? Why would this not have been taken care of in 1993?


I also seem to have hit a logical paradox:

  1. Optimization should not be done without profiling
  2. Lua has no ability to be profiled
  3. Lua should not be optimized

解决方案

Why would this be helpful? It's the job of the compiler to do that anyway. Why is the programmer having to do the compiler's job?

Lua is a dynamic language. Compilers can do a lot of reasoning in static languages, like pulling constant expressions out of the loop. In dynamic languages, the situation is a bit different.

Lua's main (and also only) data structure is the table. math is also just a table, even though it is used as a namespace here. Nobody can stop you from modifying the math.sin function somewhere in the loop (even thought that would be an unwise thing to do), and the compiler cannot know that when compiling the code. Therefore the compiler does exactly what you instruct it to do: in every iteration of the loop, lookup the sin function in the math table and call it.

Now, if YOU know that you are not going to modify math.sin (i.e. you are going to call the same function), you can save it in a local variable outside the loop. Because there are no table lookups, the resulting code is faster.

The situation is a bit different with LuaJIT - it uses tracing and some advanced magic to see what your code is doing in runtime, so it can actually optimize the loop by moving the expression outside of the loop, and other optimizations, apart from actually compiling it to machine code, making it crazy fast.

Regarding the the 'redeclaring variables as local' - many times when defining a module, you want to work with the original function. When accessing pairs, max or anything using their global variables, nobody can assure you that it will be the same function every call. For example stdlib redefines a lot of global functions.

By creating a local variable with the same name as the global, you essentially store the function into a local variable, and because local variables (which are lexically scoped, meaning they are visible in the current scope and any nested scopes too) take precedence before globals, you make sure to always call the same function. Should someone modify the global later, it will not affect your module. Not to mention it is also faster, because globals are looked up in a global table (_G).

Update: I just read Lua Performance Tips by Roberto Ierusalimschy, one of Lua authors, and it pretty much explains everything that you need to know about Lua, performance and optimization. IMO the most important rules are:

Rule #1: Don’t do it.

Rule #2: Don’t do it yet. (for experts only)

这篇关于为什么这个 Lua 优化技巧会提高性能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆