高性能且小巧的脚本语言 LuaJIT

简介: Just-In-Time or JIT 是指在程序运行时进行代码编译的技术,像 Java,Python(这里指PyPy)、LuaJIT 都引入了这种技术。

网络异常,图片无法展示
|

What's JIT?

Just-In-Time or JIT 是指在程序运行时进行代码编译的技术,像 Java,Python(这里指PyPy)、LuaJIT 都引入了这种技术。

一般 JIT 编译器与解释器一同工作,大部分时间代码由解释器进行转换成机器码进行运行,当某些代码运行的次数超过设定的阈值时,就会触发 JIT 编译进行工作,JIT 编译器会把这些热点代码编译为机器码,当下次运行到这些代码时,就不用解析器进行解释转换了,可以直接运行机器码来提高程序的运行速度。


How does work of LuaJIT?

顾名思义,LuaJIT 是一种即时(JIT) 编译器。这意味着函数是按需编译的,即当它们首先运行时。这既确保了应用程序的快速启动,也有助于避免无用的工作。例如,未使用的函数根本不会被编译。

另一种编译方法称为提前(AOT) 编译。这里所有的东西都是在运行任何函数之前编译的。这是许多语言的经典方式,例如 C、C++、Go、Rust 等等。

当启动 LuaJIT 时,一切都像在标准 Lua 中一样进行:初始化 Lua 核心,加载标准库并分析命令行。然后通常会加载第一个 Lua 源代码文件并将其转换为 Lua 字节码。最后运行初始主块的函数......

example.lua:

local s = "hello,world!"
for i=1,10000 do
    for j=1,10000 do
        string.find(s, "ll", 1, true)
    end
end

上面代码中,它会被先转换成 LuaJIT 自己定义的字节码,我们可以用下面的命令来查看:

$ luajit -bl example.lua
-- BYTECODE -- example.lua:0-8
0001    KSTR     0   0      ; "hello,world!"
0002    KSHORT   1   1
0003    KSHORT   2 10000
0004    KSHORT   3   1
0005    FORI     1 => 0019
0006 => KSHORT   5   1
0007    KSHORT   6 10000
0008    KSHORT   7   1
0009    FORI     5 => 0018
0010 => GGET     9   1      ; "string"
0011    TGETS    9   9   2  ; "find"
0012    MOV     10   0
0013    KSTR    11   3      ; "ll"
0014    KSHORT  12   1
0015    KPRI    13   2
0016    CALL     9   1   5
0017    FORL     5 => 0010
0018 => FORL     1 => 0006
0019 => RET0     0   1

然后这些字节码再交给解释器去执行,当执行达到阈值设定时,就会触发 JIT 编译器的工作,LuaJIT 会先将它转换成 IR 中间码,然后转换成对应平台的机器码


Not Yet Implemented

在 LuaJIT 中,当 JIT 编译器编译成功后就会生成一个 trace 类型的 GC 对象,但是并不是所有的代码 LuaJIT 都能够成功编译。

当 LuaJIT 遇到不支持的函数或代码(一般叫它:NYI)时,就会中止当前的编译工作,重新回退到解释器执行的模式去。

例如上面的 string.find() 函数,只有 LuaJIT 2.1 以上的版本才支持,我们可以执行添加 -jv 选项来显示有关 JIT 编译器进度的详细信息。

$ luajit -v
LuaJIT 2.0.5 -- Copyright (C) 2005-2017 Mike Pall. http://luajit.org/
$ luajit -jv example.lua
[TRACE --- example.lua:4 -- NYI: FastFunc string.find at example.lua:5]

当 LuaJIT 版本为 2.0.5 时,就会提示你 NYI: FastFunc string.find at example.lua:5,意思就是说LuaJIT 编译器不支持编译 example.lua 文件的第 5 行代码,第 5 行代码就是 string.find(s, "ll", 1, true)

当我们切换到 luajit-2.1.0-beta3 后,再执行看看:

$ luajit-2.1.0-beta3 -jv example.lua
[TRACE   1 example.lua:4 loop]
[TRACE   2 (1/3) example.lua:3 -> 1]

TRACE 后面接着是数字,说明 JIT 编译成功了

How does maintain speed of LuaJIT?

要保证 LuaJIT 的运行速度时,就要避免使用 NYI 函数,如果调用 C 函数的话,尽量使用 LuaJIT 的 ffi 库来调用 C 函数。

尽量保持使用最新的 LuaJIT 版本,LuaJIT 的 2.1 版本加入了很多原先不支持的 NYI 函数,例如 string.find() 等,所以尽量使用最新版本来提升可被 JIT 编译的函数的数量。

如果需要使用 NYI 函数时,可以去看看网上或 OpenResty 中有没有对应替代函数。

Speed Test between JIT and None-JIT

那么,有 JIT 和没有 JIT 之间的速度差异到底有大呢?

由于 LuaJIT 2.0.5 的 JIT 不支持 string.find(),不会触发 JIT 的编译工作,所以我们可以用上面的代码,然后用 LuaJIT 2.0.5LuaJIT 2.1.0-beta3 分别来执行测试下有 JIT 和 没有 JIT 之间代码执行的速度:

example.lua:

local s = "hello,world!"
for i=1,10000 do
    for j=1,10000 do
        string.find(s, "ll", 1, true)
    end
end

LuaJIT 2.0.5 的执行速度:

# 第一次测试
$ time luajit-2.0.5 example.lua
luajit-2.0.5 example.lua  2.14s user 0.00s system 99% cpu 2.140 total
# 第二次测试
$ time luajit-2.0.5 example.lua
luajit-2.0.5 example.lua  2.17s user 0.00s system 99% cpu 2.169 total
# 第三次测试
$ time luajit-2.0.5 example.lua
luajit-2.0.5 example.lua  2.17s user 0.00s system 99% cpu 2.176 total

LuaJIT 2.1.0-beta3 的执行速度:

# 第一次测试
$ time luajit-2.1.0-beta3 example.lua
luajit-2.1.0-beta3 example.lua  0.03s user 0.00s system 99% cpu 0.026 total
# 第二次测试
$ time luajit-2.1.0-beta3 example.lua
luajit-2.1.0-beta3 example.lua  0.02s user 0.00s system 99% cpu 0.024 total
# 第三次测试
$ time luajit-2.1.0-beta3 example.lua
luajit-2.1.0-beta3 example.lua  0.03s user 0.00s system 99% cpu 0.026 total

可以看到,两个的速度相差了差不多 100 倍

目录
相关文章
|
6月前
|
数据采集 前端开发 PHP
深入PHP内核:探索高性能异步编程
【2月更文挑战第24天】在现代Web开发中,提供快速响应和高并发处理能力是至关重要的。传统的同步阻塞模型已无法满足现代互联网服务的需求。本文将深入探讨PHP语言中的异步编程概念,介绍其原理、实现方式以及如何通过异步编程提升应用性能。我们将剖析PHP的多线程扩展,如pthreads,以及事件驱动编程模型,重点关注它们在处理大量并发连接时的优势和潜在问题。此外,文中还将展示如何使用Swoole等高级框架来构建高效的异步应用。最后,我们将讨论异步编程在实际应用中的挑战与未来发展趋势。
|
20天前
|
SQL 安全 关系型数据库
PHP作为一种流行的服务端脚本语言,在Web开发领域具有显著的优势
【10月更文挑战第11天】PHP作为一种流行的服务端脚本语言,在Web开发领域具有显著的优势
32 0
|
3月前
|
安全 前端开发 PHP
PHP与现代Web开发:构建高效和可扩展的应用程序
【8月更文挑战第29天】在这篇文章中,我们将深入探讨PHP如何适应现代Web开发的需求。我们将通过实际案例分析,揭示PHP的核心优势,并展示如何利用这些优势来构建高性能、可扩展的Web应用。文章不仅提供理论知识,还包括具体的代码示例,旨在帮助开发者更好地理解和运用PHP解决实际问题。
|
3月前
|
前端开发 API C++
在Ruby世界中寻找你的Web框架灵魂伴侣:Rails vs Sinatra
【8月更文挑战第31天】在Ruby的世界里,选择Web框架如同挑选衣物,需根据场合和需求。Rails与Sinatra是两大热门框架,前者以其“约定优于配置”理念和全面的功能成为企业级应用的首选;后者则以轻量级和灵活性著称,适用于快速原型开发和小规模应用。通过对比两者特性,如Rails的MVC架构与Sinatra的简洁API,我们可以看到它们各有所长。选择合适的框架,如同找到旅途中的最佳伙伴,让开发之路更加顺畅愉悦。这场探索之旅教会我们,没有绝对的好坏,只有最适合的选择。
33 0
|
3月前
|
Ruby
Ruby中的模块奥秘:混合魔法揭秘
【8月更文挑战第31天】在探索Ruby语言的过程中,我发现模块(Module)和混合(inclusion)是两个非常有用的特性。模块可以组织代码,包含方法、类和常量而不被实例化。混合则允许模块中的方法像类的方法一样被调用,提高代码复用性和灵活性。例如,可以将文本处理方法放入模块并通过`include`关键字混合到其他类中使用。此外,模块还可以在运行时动态添加到类中,按需加载功能。这些特性使代码更清晰、易于维护和扩展。希望这篇博客能让你感受到Ruby的魅力,并激发你进一步探索的兴趣。
34 0
|
5月前
|
数据挖掘 Linux 数据处理
探索Linux下的Lua命令:轻量级脚本语言在数据处理和分析中的应用
**探索Linux上的Lua:轻量级脚本语言用于数据处理。Lua通过命令行解释器执行,适用于游戏开发、数据分析及自动化。特点包括小巧、高效、可扩展和动态类型。使用`lua`或`luajit`,配合-e、-l、-i参数执行脚本或互动模式。示例:执行`hello.lua`脚本打印"Hello, Lua!"。最佳实践涉及版本兼容、性能优化、使用C API、测试和文档编写。**
|
6月前
|
缓存 监控 NoSQL
使用Elixir进行可扩展的Web服务开发
【5月更文挑战第30天】本文探讨了使用Elixir进行可扩展Web服务开发,Elixir基于Erlang/OTP,提供并发、分布式处理和容错能力。Phoenix框架助力构建实时Web应用,支持WebSocket。实现可扩展性涉及并发处理、分布式架构、数据库优化、缓存策略及监控告警。实践案例显示,Elixir和Phoenix能有效应对高并发场景,适用于构建高性能Web服务。
|
6月前
|
Java
开发语言漫谈-erlang
erlang又是一个应用邻域狭窄的语言
|
6月前
|
PHP 开发者
深入PHP内核:探索高性能脚本语言的秘诀
【4月更文挑战第28天】 在现代Web开发领域,PHP一直是一个不可或缺的服务器端脚本语言。尽管面临诸多新兴技术的挑战,PHP凭借其独特的灵活性、强大的社区支持和持续的性能优化,依然稳居市场重要地位。本文将深入探讨PHP的核心特性,分析其在性能方面的优化策略,并探讨如何利用这些策略来编写更高效的代码。我们将从PHP的执行机制入手,逐步剖析其内存管理、并发处理以及扩展性等关键技术点,为开发者提供提升应用性能的实用指导。
|
6月前
|
缓存 自然语言处理 监控
深入PHP内核:探索高性能脚本编程的秘密
【4月更文挑战第30天】 在现代Web开发中,PHP作为一种流行的服务器端脚本语言,其性能优化一直是开发者关注的焦点。本文将深入探讨PHP内核架构,分析影响PHP脚本性能的关键因素,并提出一系列提升执行效率的策略。我们将从语言解释器的角度出发,剖析词法分析、语法分析和执行机制,同时考虑内存管理和代码优化的实践技巧。通过本文的阅读,读者能够对PHP的性能调优有更深层次的理解,并在实际项目中运用这些知识以实现高效的脚本运行。