iis服务器助手广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Golang和Lua相遇会发生什么
  • 484
分享到

Golang和Lua相遇会发生什么

2023-06-25 14:06:24 484人浏览 安东尼
摘要

这篇文章主要讲解了“golang和lua相遇会发生什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang和Lua相遇会发生什么”吧!在 GitHub 玩耍时,偶然发现了 gopher

这篇文章主要讲解了“golanglua相遇会发生什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang和Lua相遇会发生什么”吧!

GitHub 玩耍时,偶然发现了 gopher-lua ,这是一个纯 Golang 实现的 Lua 虚拟机。我们知道 Golang 是静态语言,而 Lua 是动态语言,Golang 的性能和效率各语言中表现得非常不错,但在动态能力上,肯定是无法与 Lua 相比。那么如果我们能够将二者结合起来,就能综合二者各自的长处了(手动滑稽。

项目 Wiki 中,我们可以知道 gopher-lua 的执行效率和性能仅比 C 实现的 bindings 差。因此从性能方面考虑,这应该是一款非常不错的虚拟机方案。

Hello World

这里给出了一个简单的 Hello World 程序。我们先是新建了一个虚拟机,随后对其进行了 DoString(...) 解释执行 lua 代码的操作,最后将虚拟机关闭。执行程序,我们将在命令行看到 "Hello World" 的字符串

package mainimport ("github.com/yuin/gopher-lua")func main() {l := lua.NewState()defer l.Close()if err := l.DoString(`print("Hello World")`); err != nil {panic(err)}}// Hello World

提前编译

在查看上述 DoString(...) 方法的调用链后,我们发现每执行一次 DoString(...) 或 DoFile(...) ,都会各执行一次 parse 和 compile 。

func (ls *LState) DoString(source string) error {if fn, err := ls.LoadString(source); err != nil {return err} else {ls.Push(fn)return ls.PCall(0, MultRet, nil)}}func (ls *LState) LoadString(source string) (*LFunction, error) {return ls.Load(strings.NewReader(source), "<string>")}func (ls *LState) Load(reader io.Reader, name string) (*LFunction, error) {chunk, err := parse.Parse(reader, name)// ...proto, err := Compile(chunk, name)// ...}

从这一点考虑,在同份 Lua 代码将被执行多次(如在 Http server 中,每次请求将执行相同 Lua 代码)的场景下,如果我们能够对代码进行提前编译,那么应该能够减少 parse 和 compile 的开销(如果这属于 hotpath 代码)。根据 Benchmark 结果,提前编译确实能够减少不必要的开销。

package glua_testimport ("bufio""os""strings"lua "github.com/yuin/gopher-lua""github.com/yuin/gopher-lua/parse")// 编译 lua 代码字段func CompileString(source string) (*lua.FunctionProto, error) {reader := strings.NewReader(source)chunk, err := parse.Parse(reader, source)if err != nil {return nil, err}proto, err := lua.Compile(chunk, source)if err != nil {return nil, err}return proto, nil}// 编译 lua 代码文件func CompileFile(filePath string) (*lua.FunctionProto, error) {file, err := os.Open(filePath)defer file.Close()if err != nil {return nil, err}reader := bufio.NewReader(file)chunk, err := parse.Parse(reader, filePath)if err != nil {return nil, err}proto, err := lua.Compile(chunk, filePath)if err != nil {return nil, err}return proto, nil}func BenchmarkRunWithoutPreCompiling(b *testing.B) {l := lua.NewState()for i := 0; i < b.N; i++ {_ = l.DoString(`a = 1 + 1`)}l.Close()}func BenchmarkRunWithPreCompiling(b *testing.B) {l := lua.NewState()proto, _ := CompileString(`a = 1 + 1`)lfunc := l.NewFunctionFromProto(proto)for i := 0; i < b.N; i++ {l.Push(lfunc)_ = l.PCall(0, lua.MultRet, nil)}l.Close()}// goos: darwin// goarch: amd64// pkg: glua// BenchmarkRunWithoutPreCompiling-8         100000             19392 ns/op           85626 B/op         67 allocs/op// BenchmarkRunWithPreCompiling-8           1000000              1162 ns/op            2752 B/op          8 allocs/op// PASS// ok      glua    3.328s

虚拟机实例池

在同份 Lua 代码被执行的场景下,除了可使用提前编译优化性能外,我们还可以引入虚拟机实例池。

因为新建一个 Lua 虚拟机会涉及到大量的内存分配操作,如果采用每次运行都重新创建和销毁的方式的话,将消耗大量的资源。引入虚拟机实例池,能够复用虚拟机,减少不必要的开销。

func BenchmarkRunWithoutPool(b *testing.B) {for i := 0; i < b.N; i++ {l := lua.NewState()_ = l.DoString(`a = 1 + 1`)l.Close()}}func BenchmarkRunWithPool(b *testing.B) {pool := newVMPool(nil, 100)for i := 0; i < b.N; i++ {l := pool.get()_ = l.DoString(`a = 1 + 1`)pool.put(l)}}// goos: darwin// goarch: amd64// pkg: glua// BenchmarkRunWithoutPool-8          10000            129557 ns/op          262599 B/op        826 allocs/op// BenchmarkRunWithPool-8            100000             19320 ns/op           85626 B/op         67 allocs/op// PASS// ok      glua    3.467s

Benchmark 结果显示,虚拟机实例池的确能够减少很多内存分配操作。

下面给出了 README 提供的实例池实现,但注意到该实现在初始状态时,并未创建足够多的虚拟机实例(初始时,实例数为0),以及存在 slice 的动态扩容问题,这都是值得改进的地方。

type lStatePool struct {    m     sync.Mutex    saved []*lua.LState}func (pl *lStatePool) Get() *lua.LState {    pl.m.Lock()    defer pl.m.Unlock()    n := len(pl.saved)    if n == 0 {        return pl.New()    }    x := pl.saved[n-1]    pl.saved = pl.saved[0 : n-1]    return x}func (pl *lStatePool) New() *lua.LState {    L := lua.NewState()    // setting the L up here.    // load scripts, set global variables, share channels, etc...    return L}func (pl *lStatePool) Put(L *lua.LState) {    pl.m.Lock()    defer pl.m.Unlock()    pl.saved = append(pl.saved, L)}func (pl *lStatePool) Shutdown() {    for _, L := range pl.saved {        L.Close()    }}// Global LState poolvar luaPool = &lStatePool{    saved: make([]*lua.LState, 0, 4),}

模块调用

gopher-lua 支持 Lua 调用 Go 模块,个人觉得,这是一个非常令人振奋的功能点,因为在 Golang 程序开发中,我们可能设计出许多常用的模块,这种跨语言调用的机制,使得我们能够对代码、工具进行复用。

当然,除此之外,也存在 Go 调用 Lua 模块,但个人感觉后者是没啥必要的,所以在这里并没有涉及后者的内容。

package mainimport ("fmt"lua "github.com/yuin/gopher-lua")const source = `local m = require("gomodule")m.goFunc()print(m.name)`func main() {L := lua.NewState()defer L.Close()L.PreloadModule("gomodule", load)if err := L.DoString(source); err != nil {panic(err)}}func load(L *lua.LState) int {mod := L.SetFuncs(L.NewTable(), exports)L.SetField(mod, "name", lua.LString("gomodule"))L.Push(mod)return 1}var exports = map[string]lua.LGFunction{"goFunc": goFunc,}func goFunc(L *lua.LState) int {fmt.Println("golang")return 0}// golang// gomodule

变量污染

当我们使用实例池减少开销时,会引入另一个棘手的问题:由于同一个虚拟机可能会被多次执行同样的 Lua 代码,进而变动了其中的全局变量。如果代码逻辑依赖于全局变量,那么可能会出现难以预测的运行结果(这有点数据库隔离性中的“不可重复读”的味道)。

全局变量

如果我们需要限制 Lua 代码只能使用局部变量,那么站在这个出发点上,我们需要对全局变量做出限制。那问题来了,该如何实现呢?

我们知道,Lua 是编译成字节码,再被解释执行的。那么,我们可以在编译字节码的阶段中,对全局变量的使用作出限制。在查阅完 Lua 虚拟机指令后,发现涉及到全局变量的指令有两条:GETGLOBAL(Opcode 5)和 SETGLOBAL(Opcode 7)。

到这里,已经有了大致的思路:我们可通过判断字节码是否含有 GETGLOBAL 和 SETGLOBAL 进而限制代码的全局变量的使用。至于字节码的获取,可通过调用 CompileString(...) 和 CompileFile(...) ,得到 Lua 代码的 FunctionProto ,而其中的 Code 属性即为字节码 slice,类型为 []uint32 。

在虚拟机实现代码中,我们可以找到一个根据字节码输出对应 OpCode 的工具函数。

// 获取对应指令的 OpCodefunc opGetOpCode(inst uint32) int {return int(inst >> 26)}

有了这个工具函数,我们即可实现对全局变量的检查。

package main// ...func CheckGlobal(proto *lua.FunctionProto) error {for _, code := range proto.Code {switch opGetOpCode(code) {case lua.OP_GETGLOBAL:return errors.New("not allow to access global")case lua.OP_SETGLOBAL:return errors.New("not allow to set global")}}// 对嵌套函数进行全局变量的检查for _, nestedProto := range proto.FunctionPrototypes {if err := CheckGlobal(nestedProto); err != nil {return err}}return nil}func TestCheckGetGlobal(t *testing.T) {l := lua.NewState()proto, _ := CompileString(`print(_G)`)if err := CheckGlobal(proto); err == nil {t.Fail()}l.Close()}func TestCheckSetGlobal(t *testing.T) {l := lua.NewState()proto, _ := CompileString(`_G = {}`)if err := CheckGlobal(proto); err == nil {t.Fail()}l.Close()}

模块

除变量可能被污染外,导入的 Go 模块也有可能在运行期间被篡改。因此,我们需要一种机制,确保导入到虚拟机的模块不被篡改,即导入的对象是只读的。

在查阅相关博客后,我们可以对 Table 的 __newindex 方法的修改,将模块设置为只读模式。

package mainimport ("fmt""github.com/yuin/gopher-lua")// 设置表为只读func SetReadOnly(l *lua.LState, table *lua.LTable) *lua.LUserData {ud := l.NewUserData()mt := l.NewTable()// 设置表中域的指向为 tablel.SetField(mt, "__index", table)// 限制对表的更新操作l.SetField(mt, "__newindex", l.NewFunction(func(state *lua.LState) int {state.RaiseError("not allow to modify table")return 0}))ud.Metatable = mtreturn ud}func load(l *lua.LState) int {mod := l.SetFuncs(l.NewTable(), exports)l.SetField(mod, "name", lua.LString("gomodule"))// 设置只读l.Push(SetReadOnly(l, mod))return 1}var exports = map[string]lua.LGFunction{"goFunc": goFunc,}func goFunc(l *lua.LState) int {fmt.Println("golang")return 0}func main() {l := lua.NewState()l.PreloadModule("gomodule", load)    // 尝试修改导入的模块if err := l.DoString(`local m = require("gomodule");m.name = "hello world"`); err != nil {fmt.Println(err)}l.Close()}// <string>:1: not allow to modify table

感谢各位的阅读,以上就是“Golang和Lua相遇会发生什么”的内容了,经过本文的学习后,相信大家对Golang和Lua相遇会发生什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

您可能感兴趣的文档:

--结束END--

本文标题: Golang和Lua相遇会发生什么

本文链接: https://www.lsjlt.com/news/305210.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Golang和Lua相遇会发生什么
    这篇文章主要讲解了“Golang和Lua相遇会发生什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang和Lua相遇会发生什么”吧!在 GitHub 玩耍时,偶然发现了 gopher...
    99+
    2023-06-25
  • 并行Stream与Spring事务相遇会发生什么?
    目录事务不生效的代码JDK 8 的Stream@Transactional事务处理Bug综合分析问题拓展小结前言: 事情是这样的:运营人员反馈,通过Excel导入数据时,有一部分成功...
    99+
    2024-04-02
  • golang和什么相似
    golang和c语言相似。Go语言被描述为“C 类似语言”,或者是“21 世纪的C语言”,其语法与C相近;Go从C语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等很多思想,还有C语言一直所看中的编译后机器码的运行效...
    99+
    2023-05-14
    go语言 Golang
  • 当 MySQL 遇到超出范围的日期时会发生什么?
    MySQL 在遇到超出范围或无效日期时的响应将取决于 SQL MODE。如果我们启用了 ALLOW_INVALID_DATES 模式,那么 MySQL 会将超出范围的值转换为全零(即“0000:00:00 00:00:00”)并将其存储在表...
    99+
    2023-10-22
  • 聊聊golang和什么相似
    Golang 和什么相似作为近几年备受瞩目的编程语言,Golang(又称Go)在各个领域都有着广泛的应用。那么,Golang 到底和什么有着相似之处呢?本文尝试从不同角度探讨 Golang 的相似之处。C语言Golang 的设计初衷是为了替...
    99+
    2023-05-14
  • Redis高延迟时会发生什么
    这篇文章将为大家详细讲解有关Redis高延迟时会发生什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Redis 是一种内存数据库,将数据保存在内存中,读写...
    99+
    2024-04-02
  • 输入URL后页面会发生什么
    这篇文章主要介绍“输入URL后页面会发生什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“输入URL后页面会发生什么”文章能帮助大家解决问题。构建 DOM 树由于浏览器无法直接理解 HTML字符串 ...
    99+
    2023-06-27
  • Java进程转移前会发生什么
    本篇内容主要讲解“Java进程转移前会发生什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java进程转移前会发生什么”吧!1、系统当前网络连接ss -antp >&...
    99+
    2023-06-16
  • 使用React Native/Redux开发中会遇到什么错误
    这篇文章主要为大家展示了“使用React Native/Redux开发中会遇到什么错误”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“使用React Native...
    99+
    2024-04-02
  • golang怎么根据生日计算星座和属相
    今天小编给大家分享一下golang怎么根据生日计算星座和属相的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。根据生日计算星座f...
    99+
    2023-07-02
  • Ajax中get和post使用会遇到什么问题
    这篇文章将为大家详细讲解有关Ajax中get和post使用会遇到什么问题,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。使用get遇到的问题:1.问题一. 缓存:当每次访问...
    99+
    2024-04-02
  • JavaScript什么时候会发生自动转换
    这篇文章主要介绍了JavaScript什么时候会发生自动转换,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。JavaScript可以自由的进行数据类型转换,也提供了多种显式转换...
    99+
    2023-06-29
  • 如果 MySQL 会话结束,MySQL 临时表会发生什么?
    如果 MySQL 会话终止,临时表将被删除。再次登录后,在发出 SELECT 命令时,我们将发现数据库中没有可用数据。甚至我们的临时表也不存在。...
    99+
    2023-10-22
  • 输入网址按回车会发生什么
    这篇文章主要介绍“输入网址按回车会发生什么”,在日常操作中,相信很多人在输入网址按回车会发生什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”输入网址按回车会发生什么”的疑惑有所帮助!接下来,请跟着小编一起来...
    99+
    2023-06-16
  • php die函数不传参会发生什么
    今天小编给大家分享一下php die函数不传参会发生什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。php die函数不传...
    99+
    2023-06-30
  • 服务器托管为什么会发生故障
    服务器托管发生故障的原因有:1、由于客户远程操作失误而导致服务器托管发生故障;2、在服务器上运行多种应用服务,导致某种服务无法启动或死机,从而导致服务器托管发生故障;3、服务器硬件出现问题,包括主板、内存、硬盘等方面;4、服务器访问量过大,...
    99+
    2024-04-02
  • 使用taro开发微信小程序会遇到什么问题
    这篇文章主要介绍了使用taro开发微信小程序会遇到什么问题,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Taro,京东凹凸实验室出品的适配多...
    99+
    2024-04-02
  • 教育行业结合小程序会发生什么
    本篇内容介绍了“教育行业结合小程序会发生什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!传统教育行业的改变:1.不在场景限制我们都很清楚,...
    99+
    2023-06-27
  • 在浏览器中输入URL后都会发生什么
    用户输入URL,会使用浏览器默认搜索引擎加上搜索内容合成url;如果是域名会加上协议(如https)合成完整的url。 然后按下回车。浏览器进程通过进程间通信把url传给网络进程。(网络进程接收到ur...
    99+
    2023-08-31
    网络 服务器 tcp/ip
  • 您不会相信Vue和Less可以一起做些什么!
    Vue.js 和 Less.js 是两个功能强大的前端框架,可以帮助您创建美观、交互友好的网站和应用程序。Vue.js 是一个用于构建用户界面的 JavaScript 框架,它提供了一个简单的 API,用于创建响应式和可重用的组件。Le...
    99+
    2024-02-14
    Vue.js, Less.js, JavaScript, CSS, 前端开发, 网页设计
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作