在Lua中的本地与全局
每个来源都认为这一点是正确的:局部变量的访问比全局变量快。
在实际使用中,主要区别在于如何处理变量,因为它的范围是有限的,不可从代码的任何地方访问。
在理论上,局部变量不受非法更改的影响,因为它不可从错误的地方访问,并且查找该变量的性能更高。
现在我想知道这个概念的细节;一些代码部分如何访问,另外一些代码部分如何访问不到?性能的改进如何?
但最重要的问题是:假设我有一个变量 bazinga =“So cool.”,并且想要从任何地方更改它。由于字符串是公共的,我可以轻松这样做。但是,现在如果它被声明为局部的,并且我超出了范围,如果我通过X个函数传递变量,那么需要进行多少性能努力才能访问它?
func_3(bazinga)
func_N(bazinga)
end
func_2(bazinga)
func_3(bazinga)
end
func_1()
local bazinga =“So cool。”
func_2(bazinga)
end
到哪个点,局部变量仍然更具有性能优势,为什么?
我问你,由于维护在其中对象被传递多个函数的代码会变得混乱,我想知道它是否真的值得。
在理论上,局部变量由于不能从错误的位置访问和查找变量的可靠性更高而免受非法更改的影响。
实际上,局部变量在实际中并不安全。这个概念是_lexical scoping_的一部分 - 这是一种名称解析方法,相较于动态和/或纯全局作用域而言,具有一些优势(如果你喜欢,也有缺点)。
性能的根源在于在Lua中,局部变量只是堆栈槽,按整数偏移索引,编译时(即在load()时)计算一次。但是全局变量实际上是全局表中的键,这是非常常规的表,因此任何访问都是非预先计算的查找。所有这些都取决于实现细节,可能因不同的语言甚至实现而异(如有人已经指出的,LuaJIT可以优化许多事情,因此因人而异)。
现在我想知道这个概念的细节;一些代码部分如何在技术上访问,而其他一些代码部分则无法访问?性能提高了多少?
从技术上讲,在5.1中,全局变量是具有特殊操作码的特殊表,而5.2删除了全局操作码并引入了每个函数的\_ENV upvalue。(我们所说的全局实际上是环境变量,因为查找进入函数的环境,该环境可能设置为除“全局表”之外的值,但让我们不要在飞行中更改术语)。所以,用5.2的术语来说,任何全局都只是全局表中的键值对,在每个函数中通过一个词法范围变量都可以访问。
现在谈到局部和词法作用域。正如您已经知道的,局部变量是堆栈槽。但是如果我们的函数使用外部范围的变量呢?在这种情况下,会创建一个特殊块来保存变量,它变成了_upvalue_。Upvalue是指向原始变量的无缝指针,当它的范围结束时,防止它被销毁(本地变量通常在逃逸范围时将不再存在,对吧?)。
但是主要的问题是:假设我有一个变量bazinga =“So cool.”,并且想要从某个地方进行更改。由于该字符串是公共的,所以我可以轻松地做到这一点。但是现在,如果它被声明为局部变量,而我不在范围内,则需要进行访问的性能努力是多少,如果我通过像这样的X函数交出变量:
在您的片段中,传递下调用堆栈的不是变量,而是值"So cool."(它是指向堆的指针,就像所有其他可垃圾回收值一样)。局部变量bazinga从未传递给任何函数,因为Lua没有称为var-parameters(Pascal)或指针/引用(C/C++) 的概念。每次调用函数时,所有参数都变成其局部变量,在我们的情况下,bazinga不是单个变量,而是具有相同值的不同堆栈帧中的一堆堆栈槽 - 指向堆的相同指针,其中包含"So cool."字符串。因此,在每个调用堆栈级别上都没有任何惩罚。
你可以使用 os.clock() 自己测试局部变量和全局变量的性能。以下代码测试于一个运行在虚拟机上的 2.8 GHz 四核环境中。
-- 分配内存
local start = os.clock()
local timeend = os.clock()
local diff = timeend - start
local difflocal = {}
local diffglobal = {}
local x, y = 1, 1 -- 局部变量
a, b = 1, 1 -- 全局变量
-- 进行 10 次测试
for i = 0, 10, 1 do
-- 开始测试局部变量
start = os.clock()
for ii = 0, 100000, 1 do
y = y + ii
x = x + ii
end
timeend = os.clock()
-- 停止测试局部变量
diff = (timeend - start) * 1000
table.insert(difflocal, diff)
-- 开始测试全局变量
start = os.clock()
for ii = 0, 100000, 1 do
b = b + ii
a = a + ii
end
timeend = os.clock()
-- 停止测试全局变量
diff = (timeend - start) * 1000
table.insert(diffglobal, diff)
end
print(a)
print(b)
print(table.concat(difflocal, " ms, "))
print(table.concat(diffglobal, " ms, "))
输出:
55000550001
55000550001
2.033 ms, 1.979 ms, 1.97 ms, 1.952 ms, 1.914 ms, 2.522 ms, 1.944 ms, 2.121 ms, 2.099 ms, 1.923 ms, 2.12
9.649 ms, 9.402 ms, 9.572 ms, 9.286 ms, 8.767 ms, 10.254 ms, 9.351 ms, 9.316 ms, 9.936 ms, 9.587 ms, 9.58
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
在进行任何比较之前,我要提醒您,您的担忧可能过早:先写代码,然后进行性能分析,然后再进行优化。在某些情况下在事后优化可能会很困难,但这不太可能是这种情况。
访问本地变量比访问全局变量快,因为访问全局变量会包括表查找(无论是在
_G还是在_ENV中)。 LuaJIT 可能会优化其中的一些表访问,因此差异可能在那里不太明显。在这种情况下,您无需牺牲访问的便捷性,因为您始终可以使用从函数到上值的访问来使本地变量可用:
local myvar function getvar() return myvar end function setvar(val) myvar = val end -- elsewhere setvar('foo') print(getvar()) -- 输出 'foo'使用
getvar访问不会比作为全局变量访问myvar更快,但这使您可以使用myvar作为本地变量,并仍然可以从其他文件访问它(这可能是您希望它成为全局变量的原因)。