lua-language-server icon indicating copy to clipboard operation
lua-language-server copied to clipboard

[Experimental] Luals rust-based luajit port

Open CppCXY opened this issue 1 year ago • 34 comments

The current LuaJIT porting is complete. jit.on is not enabled because jit.off performs better. Feel free to try it if you're interested.

see: https://github.com/LuaLS/lua-language-server-rust

CppCXY avatar Sep 30 '24 07:09 CppCXY

Here are some old issues which suggested porting to luajit, maybe participants inside will be interested to test this out

  • https://github.com/LuaLS/lua-language-server/issues/261
  • https://github.com/LuaLS/lua-language-server/issues/2482
  • https://github.com/LuaLS/lua-language-server/issues/2781

tomlau10 avatar Sep 30 '24 13:09 tomlau10

How did you test with LuaJIT? Did you remove all <close> variables?

lewis6991 avatar Sep 30 '24 13:09 lewis6991

How did you test with LuaJIT? Did you remove all <close> variables?

I replace close to defer : image

CppCXY avatar Sep 30 '24 14:09 CppCXY

I just did a preliminary benchmark here: https://github.com/LuaLS/lua-language-server/issues/2482#issuecomment-2385017356

  • The compile time of a large lua file (~8K lines) is 40% faster 🚀
  • But the textDocument/documentSymbol of that file is 100% slower 🐌 ... and currently I have no idea why 😕

tomlau10 avatar Oct 01 '24 07:10 tomlau10

I just did a preliminary benchmark here: #2482 (comment)

  • The compile time of a large lua file (~8K lines) is 40% faster 🚀
  • But the textDocument/documentSymbol of that file is 100% slower 🐌 ... and currently I have no idea why 😕

I have already fixed many issues related to the APIs. Please check the problems again

CppCXY avatar Oct 01 '24 09:10 CppCXY

I have already fixed many issues related to the APIs. Please check the problems again

I checked out the latest commit https://github.com/LuaLS/lua-language-server-rust/commit/6c7bab8189cc6d9e18062e403d862cebbe05b1e2 and rebuilt the luals-rust, now textDocument/documentSymbol is much faster than before 👍

[18:17:04.943][warn] [#0:resources/override_script/proto\proto.lua:189]: Method [textDocument/documentSymbol] takes [1.401]sec. {
[18:17:05.041][warn] [#0:resources/override_script/proto\proto.lua:189]: Method [textDocument/documentSymbol] takes [0.684]sec. {
[18:17:27.900][warn] [#0:resources/override_script/proto\proto.lua:189]: Method [textDocument/documentSymbol] takes [0.801]sec. {
[18:17:28.008][warn] [#0:resources/override_script/proto\proto.lua:189]: Method [textDocument/documentSymbol] takes [0.903]sec. {
[18:17:28.149][warn] [#0:resources/override_script/proto\proto.lua:189]: Method [textDocument/documentSymbol] takes [0.906]sec. {
[18:17:44.449][warn] [#0:resources/override_script/proto\proto.lua:189]: Method [textDocument/documentSymbol] takes [0.643]sec. {
[18:17:44.617][warn] [#0:resources/override_script/proto\proto.lua:189]: Method [textDocument/documentSymbol] takes [0.762]sec. {
[18:17:45.654][warn] [#0:resources/override_script/proto\proto.lua:189]: Method [textDocument/documentSymbol] takes [0.844]sec. {
[18:17:55.249][warn] [#0:resources/override_script/proto\proto.lua:189]: Method [textDocument/documentSymbol] takes [0.662]sec. {
[18:17:55.695][warn] [#0:resources/override_script/proto\proto.lua:189]: Method [textDocument/documentSymbol] takes [0.569]sec. {
[18:18:03.385][warn] [#0:resources/override_script/proto\proto.lua:189]: Method [textDocument/documentSymbol] takes [0.593]sec. {
  • avg 0.789s
  • but it's just on par with regular luals 0.71s 🤔 (a little bit slower to be exact)

tomlau10 avatar Oct 01 '24 10:10 tomlau10

  • but it's just on par with regular luals 0.71s 🤔 (a little bit slower to be exact)

Is there any comprehensive performance report, such as a comparison of memory usage and startup time for large codebases?

CppCXY avatar Oct 01 '24 10:10 CppCXY

Is there any comprehensive performance report, such as a comparison of memory usage and startup time for large codebases?

I don't have that currently, I only tested it on a large file (the large_file.lua generated by the python script mentioned in the above comment). Because with the compile time benchmark of a large file, we may directly see the performance gain (if any) by switching to luajit.

I might try to do a more comprehensive performance comparison on some of my team's projects later 🤔

tomlau10 avatar Oct 01 '24 10:10 tomlau10

By conducting startup tests on the luals project itself, the manually measured results are roughly as follows. It seems to be getting slower and slower, possibly due to my own limited skills. I look forward to others' tests. I will go back to working on my own language server.

language server memory start time
luals 300mb 10s
luals-rust-lua5.4 315mb 10s
luals-rust-luajit 330mb 13.86s

CppCXY avatar Oct 01 '24 15:10 CppCXY

By conducting startup tests on the luals project itself

May I ask how did you conduct the start time test? By checking the timestamp in the log file? 😕

  • I tried to open a new vscode windows with luals project workspace (without opening any file),
  • then open the main.lua the project to let luals startup in this workspace.
  • Afterwards measure the timestamp between the following 2 logs:
[09:24:19.327][info] [#0:script\workspace\workspace.lua:38]: Workspace init root: 	file:///c%3A/Users/TomLau/lua-language-server
...
[09:24:21.968][warn] [#0:script\proto\proto.lua:189]: Method [textDocument/didOpen] takes [2.414]sec. {

Doing 5 runs on each server setup:

Start up time (in second)

server 1 2 3 4 5 avg
luals-3.11 2.422 2.62 3.13 3.134 2.691 2.7994
luals-rust-luajit 3.856 3.749 3.367 3.542 3.852 3.6732
luals-rust-luajit-jitoff 2.3 2.456 2.95 2.114 2.404 2.4448

Memory usage (in MB)

server 1 2 3 4 5 avg
luals-3.11 152 152 156 155 142 151.4
luals-rust-luajit 175 173 179 179 185 178.2
luals-rust-luajit-jitoff 129 127 127 131 128 128.4
  • I have tried to do the above test with jit.off() as well. I added it in the main.lua in luals-rust project
require "luajitCompact"
local fs      = require 'bee.filesystem'
local util    = require 'utility'
local version = require 'version'
require 'config.env'

jit.off()
jit.flush()
  • ‼️ surprisingly it runs FASTER when jit.off() 🤔 maybe the startup logic of luals is not jit-friendly...
  • ref for turning off jit may improve performance: https://www.cnblogs.com/zhaolaosan/p/16185249.html

tomlau10 avatar Oct 02 '24 02:10 tomlau10

amazing test result, I also got the same speed test results on luals-3.11: image on luajit jit-off: image

The speed improvement is about 40%. It might be faster if we can identify what is causing the JIT fail

CppCXY avatar Oct 02 '24 02:10 CppCXY

The porting to LuaJIT has been completed, and the luajit branch has been merged into the main trunk. You can download it from the release, and I have already migrated to LuaLS version 3.11.

CppCXY avatar Oct 02 '24 06:10 CppCXY

I just learnt that luajit has parameters for tuning:

  • The suggested params from these posts:
    • https://www.reddit.com/r/lua/comments/htqn0t/luajit_once_again_nearly_as_fast_as_the/
    • https://www.freelists.org/post/luajit/Inexplicable-behaviour-of-JIT-sudden-speeddown-of-the-same-code-that-was-running-fast-at-first-in-a-longrunning-Lua-program,2
  • maxtrace = 100'000 (instead of 1'000)
  • maxrecord = 40'000 (instead of 4'000)
  • maxside = 1'000 (instead of 100)
  • sizemcode = 64 (instead of 32)
  • maxmcode = 4'096 (instead of 512)
  • jit.opt.* — JIT compiler optimization control:
    • https://luajit.org/ext_jit.html
    • list of parameters: https://luajit.org/running.html
  • So it maybe called like this in the code
jit.opt.start(
    "maxtrace=100000",
    "maxrecord=40000",
    "maxside=1000",
    "sizemcode=64",
    "maxmcode=4096"
)

Maybe we can try fiddle them around and test the difference 🤔

tomlau10 avatar Oct 02 '24 07:10 tomlau10

Another test by performing full workspace diagnostic on luals project (for clarity I mean the lua-language-server, but not lua-language-server-rust). The measurement is just the time taken reported in this log:

[09:03:32.727][info] [#0:resources/override_script/provider\diagnostic.lua:566]: Diagnostics scope [file:///c%3A/Users/TomLau/lua-language-server], files count:[403]
...
[09:03:42.224][info] [#0:resources/override_script/provider\diagnostic.lua:583]: Diagnostics scope [file:///c%3A/Users/TomLau/lua-language-server] finished, takes [9.497] sec.

diagnostic time (in seconds)

server 1 2 3 4 5 avg
luals-3.11 12.87 12.668 11.613 11.572 11.687 12.082
luajit-jitoff 9.497 8.598 8.153 8.112 7.975 8.467
luajit-jiton 21.27 18.919 19.036 19.036 19.025 19.4572
luajit-jiton-tune 14.527 12.945 12.941 13.045 13.001 13.2918
  • luajit-jiton-tune is using the jit.opt.start params mentioned in the comment: https://github.com/LuaLS/lua-language-server/issues/2879#issuecomment-2387850765, I add it in the luajitCompact.lua in luals-rust project
  • but jiton-tune is still slower than regular luals-3.11
  • in short jitoff is the fastest for codebase in current state, and jiton with default params is the slowest.

tomlau10 avatar Oct 03 '24 01:10 tomlau10

It would be interesting to know the OS & CPU architecture you both run those benchmarks on. This may be a case of: https://github.com/LuaJIT/LuaJIT/issues/285

C3pa avatar Oct 03 '24 07:10 C3pa

my system spec:

  • CPU: Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
  • Windows 10 64bit 19045.4170

tomlau10 avatar Oct 03 '24 09:10 tomlau10

Maybe we can use the jit.dump module to see the reasons for jit failure and then consider targeted optimizations. I see many traces about inspect and luadoc.

CppCXY avatar Oct 03 '24 09:10 CppCXY

Try this version: https://github.com/openresty/luajit2

sumneko avatar Oct 03 '24 12:10 sumneko

Just to note that calls into C code cannot be JIT compiled, so if LuaLS calls into C a lot (into bee.lua), then it's likely the JIT isn't able to do much.

lewis6991 avatar Oct 03 '24 13:10 lewis6991

Just to note that calls into C code cannot be JIT compiled, so if LuaLS calls into C a lot (into bee.lua), then it's likely the JIT isn't able to do much.

The current situation is that JIT has caused a significant performance drop. By analyzing the results of the JIT dump, I obtained this chart: image

CppCXY avatar Oct 03 '24 13:10 CppCXY

Just to note that calls into C code cannot be JIT compiled, so if LuaLS calls into C a lot (into bee.lua), then it's likely the JIT isn't able to do much.

Are you sure about this? I am pretty sure that it can be JIT compiled, the C calls become inlined

Frityet avatar Oct 03 '24 13:10 Frityet

here are the simple:

File: luadoc.lua, Line: 939, Error: NYI: bytecode FNEW  , Count: 405
File: compile.lua, Line: 604, Error: inner loop in root trace, Count: 280
File: guide.lua, Line: 748, Error: NYI: bytecode UCLO  , Count: 233
File: compile.lua, Line: 409, Error: NYI: bytecode FNEW  , Count: 182
File: variable.lua, Line: 55, Error: NYI: bytecode UCLO  , Count: 141
File: scope.lua, Line: 214, Error: inner loop in root trace, Count: 107
File: compile.lua, Line: 604, Error: loop unroll limit reached, Count: 88
File: luadoc.lua, Line: 1822, Error: NYI: bytecode FNEW  , Count: 78
File: compile.lua, Line: 606, Error: leaving loop in root trace, Count: 55
File: compile.lua, Line: 2759, Error: leaving loop in root trace, Count: 37
File: await.lua, Line: 65, Error: leaving loop in root trace, Count: 33
File: global.lua, Line: 167, Error: NYI: bytecode FNEW  , Count: 33
File: files.lua, Line: 981, Error: NYI: bytecode FNEW  , Count: 32
File: luadoc.lua, Line: 824, Error: inner loop in root trace, Count: 29
File: luadoc.lua, Line: 1832, Error: NYI: bytecode UCLO  , Count: 28
File: luadoc.lua, Line: 955, Error: NYI: bytecode UCLO  , Count: 27
File: luadoc.lua, Line: 870, Error: inner loop in root trace, Count: 26
File: guide.lua, Line: 746, Error: NYI: bytecode FNEW  , Count: 26
File: luadoc.lua, Line: 1854, Error: inner loop in root trace, Count: 25
File: luadoc.lua, Line: 830, Error: leaving loop in root trace, Count: 25
File: luadoc.lua, Line: 2091, Error: leaving loop in root trace, Count: 25
File: guide.lua, Line: 683, Error: loop unroll limit reached, Count: 25
File: guide.lua, Line: 779, Error: loop unroll limit reached, Count: 25
File: luadoc.lua, Line: 837, Error: leaving loop in root trace, Count: 23
File: compile.lua, Line: 2090, Error: leaving loop in root trace, Count: 23
File: luadoc.lua, Line: 568, Error: NYI: bytecode FNEW  , Count: 22
File: loading.lua, Line: 39, Error: NYI: bytecode UCLO  , Count: 22
File: luadoc.lua, Line: 1946, Error: leaving loop in root trace, Count: 22
File: compile.lua, Line: 1790, Error: inner loop in root trace, Count: 21
File: luadoc.lua, Line: 824, Error: loop unroll limit reached, Count: 20
File: luadoc.lua, Line: 2003, Error: leaving loop in root trace, Count: 20
File: 0x01d10bece3a0, Line: 4, Error: leaving loop in root trace, Count: 19
File: guide.lua, Line: 768, Error: leaving loop in root trace, Count: 18
File: compile.lua, Line: 1488, Error: inner loop in root trace, Count: 18
File: utility.lua, Line: 229, Error: NYI: bytecode FNEW  , Count: 17
File: compile.lua, Line: 2161, Error: inner loop in root trace, Count: 17
File: log.lua, Line: 61, Error: leaving loop in root trace, Count: 16
File: luadoc.lua, Line: 2078, Error: leaving loop in root trace, Count: 16
File: luadoc.lua, Line: 1829, Error: NYI: bytecode FNEW  , Count: 15
File: guide.lua, Line: 796, Error: inner loop in root trace, Count: 14
File: compile.lua, Line: 1993, Error: leaving loop in root trace, Count: 14
File: luadoc.lua, Line: 2016, Error: leaving loop in root trace, Count: 13
File: guide.lua, Line: 456, Error: inner loop in root trace, Count: 13
File: compile.lua, Line: 2727, Error: inner loop in root trace, Count: 13
File: diagnostic.lua, Line: 433, Error: NYI: bytecode UCLO  , Count: 12
File: luadoc.lua, Line: 1832, Error: leaving loop in root trace, Count: 12
File: 0x01d1086d96a0, Line: 5, Error: leaving loop in root trace, Count: 12
File: guide.lua, Line: 808, Error: loop unroll limit reached, Count: 11
File: inspect.lua, Line: 296, Error: down-recursion, restarting, Count: 10
File: workspace.lua, Line: 92, Error: NYI: bytecode UCLO  , Count: 10
File: compile.lua, Line: 2759, Error: down-recursion, restarting, Count: 10
File: guide.lua, Line: 796, Error: loop unroll limit reached, Count: 10
File: inspect.lua, Line: 148, Error: persistent type instability, Count: 9
File: library.lua, Line: 135, Error: leaving loop in root trace, Count: 9
File: diagnostic.lua, Line: 456, Error: NYI: bytecode UCLO  , Count: 9
File: luadoc.lua, Line: 1152, Error: NYI: bytecode FNEW  , Count: 9
File: 0x01d17ec3ab60, Line: 9, Error: leaving loop in root trace, Count: 9
File: compile.lua, Line: 978, Error: inner loop in root trace, Count: 9
File: compile.lua, Line: 2080, Error: inner loop in root trace, Count: 9
File: inspect.lua, Line: 148, Error: loop unroll limit reached, Count: 8
File: inspect.lua, Line: 255, Error: inner loop in root trace, Count: 8
File: net.lua, Line: 282, Error: leaving loop in root trace, Count: 8
File: utility.lua, Line: 652, Error: NYI: bytecode FNEW  , Count: 8
File: 0x01d1090ab5a0, Line: 4, Error: leaving loop in root trace, Count: 8
File: luadoc.lua, Line: 2008, Error: leaving loop in root trace, Count: 8
File: compile.lua, Line: 3655, Error: leaving loop in root trace, Count: 8
File: luadoc.lua, Line: 2030, Error: inner loop in root trace, Count: 7
File: json.lua, Line: 180, Error: inner loop in root trace, Count: 7
File: 0x01d1090b85a0, Line: 4, Error: leaving loop in root trace, Count: 7
File: compile.lua, Line: 2279, Error: leaving loop in root trace, Count: 7
File: compile.lua, Line: 2185, Error: leaving loop in root trace, Count: 7
File: 0x01d10c8a9aa0, Line: 5, Error: leaving loop in root trace, Count: 7
File: pub.lua, Line: 193, Error: leaving loop in root trace, Count: 7
File: compile.lua, Line: 2161, Error: loop unroll limit reached, Count: 7
File: files.lua, Line: 939, Error: NYI: bytecode FNEW  , Count: 7
File: inspect.lua, Line: 141, Error: call unroll limit reached, Count: 6
File: files.lua, Line: 983, Error: leaving loop in root trace, Count: 6
File: scope.lua, Line: 218, Error: leaving loop in root trace, Count: 6
File: json.lua, Line: 200, Error: leaving loop in root trace, Count: 6
File: global.lua, Line: 707, Error: NYI: bytecode FNEW  , Count: 6
File: luadoc.lua, Line: 1972, Error: leaving loop in root trace, Count: 6
File: 0x01d10bed0010, Line: 4, Error: leaving loop in root trace, Count: 6
File: luadoc.lua, Line: 1853, Error: leaving loop in root trace, Count: 6
File: compile.lua, Line: 2011, Error: down-recursion, restarting, Count: 6
File: compile.lua, Line: 3471, Error: inner loop in root trace, Count: 6
File: inspect.lua, Line: 148, Error: inner loop in root trace, Count: 5
File: inspect.lua, Line: 137, Error: leaving loop in root trace, Count: 5
File: scope.lua, Line: 74, Error: inner loop in root trace, Count: 5
File: luadoc.lua, Line: 1095, Error: inner loop in root trace, Count: 5
File: compile.lua, Line: 1546, Error: leaving loop in root trace, Count: 5
File: 0x01d1090b8dc0, Line: 3, Error: loop unroll limit reached, Count: 5
File: compile.lua, Line: 3498, Error: leaving loop in root trace, Count: 5
File: compile.lua, Line: 3471, Error: loop unroll limit reached, Count: 5
File: compile.lua, Line: 2199, Error: loop unroll limit reached, Count: 5
File: provider.lua, Line: 80, Error: NYI: bytecode FNEW  , Count: 4
File: inspect.lua, Line: 151, Error: leaving loop in root trace, Count: 4
File: scope.lua, Line: 232, Error: leaving loop in root trace, Count: 4
File: luadoc.lua, Line: 2023, Error: leaving loop in root trace, Count: 4
File: scope.lua, Line: 75, Error: inner loop in root trace, Count: 4
File: luadoc.lua, Line: 2009, Error: inner loop in root trace, Count: 4
File: compile.lua, Line: 2075, Error: inner loop in root trace, Count: 4
File: guide.lua, Line: 532, Error: loop unroll limit reached, Count: 4
File: compile.lua, Line: 721, Error: leaving loop in root trace, Count: 4
File: compile.lua, Line: 2080, Error: persistent type instability, Count: 4
File: 0x01d1090b9100, Line: 4, Error: leaving loop in root trace, Count: 4
File: guide.lua, Line: 337, Error: inner loop in root trace, Count: 4
File: 0x01d1086daf00, Line: 3, Error: loop unroll limit reached, Count: 4
File: variable.lua, Line: 392, Error: leaving loop in root trace, Count: 4
File: variable.lua, Line: 71, Error: leaving loop in root trace, Count: 4
File: compile.lua, Line: 1144, Error: leaving loop in root trace, Count: 4
File: compile.lua, Line: 3908, Error: loop unroll limit reached, Count: 4
File: compile.lua, Line: 3505, Error: down-recursion, restarting, Count: 4
File: guide.lua, Line: 532, Error: persistent type instability, Count: 4
File: compile.lua, Line: 1481, Error: leaving loop in root trace, Count: 4
File: compile.lua, Line: 1593, Error: inner loop in root trace, Count: 4
File: luadoc.lua, Line: 1911, Error: inner loop in root trace, Count: 4
File: guide.lua, Line: 783, Error: leaving loop in root trace, Count: 4
File: 0x01d1086d96a0, Line: 4, Error: loop unroll limit reached, Count: 4
File: luadoc.lua, Line: 1795, Error: inner loop in root trace, Count: 4
File: compile.lua, Line: 302, Error: loop unroll limit reached, Count: 4
File: luadoc.lua, Line: 2132, Error: NYI: bytecode FNEW  , Count: 4
File: gitignore.lua, Line: 76, Error: leaving loop in root trace, Count: 3
File: workspace.lua, Line: 103, Error: NYI: bytecode FNEW  , Count: 3
File: files.lua, Line: 979, Error: inner loop in root trace, Count: 3
File: json.lua, Line: 185, Error: leaving loop in root trace, Count: 3
File: json.lua, Line: 113, Error: leaving loop in root trace, Count: 3
File: compile.lua, Line: 2727, Error: loop unroll limit reached, Count: 3
File: compile.lua, Line: 978, Error: loop unroll limit reached, Count: 3
File: 0x01d10becdec0, Line: 4, Error: leaving loop in root trace, Count: 3
File: compile.lua, Line: 2095, Error: leaving loop in root trace, Count: 3
File: luadoc.lua, Line: 2089, Error: inner loop in root trace, Count: 3
File: global.lua, Line: 184, Error: inner loop in root trace, Count: 3
File: luadoc.lua, Line: 2163, Error: NYI: bytecode UCLO  , Count: 3
File: 0x01d1086daf00, Line: 6, Error: leaving loop in root trace, Count: 3
File: compile.lua, Line: 658, Error: inner loop in root trace, Count: 3
File: compile.lua, Line: 3184, Error: leaving loop in root trace, Count: 3
File: 0x01d10b89bc90, Line: 8, Error: leaving loop in root trace, Count: 3
File: compile.lua, Line: 3920, Error: loop unroll limit reached, Count: 3
File: 0x01d10c8a98c0, Line: 5, Error: leaving loop in root trace, Count: 3
File: compile.lua, Line: 577, Error: leaving loop in root trace, Count: 3
File: compile.lua, Line: 414, Error: inner loop in root trace, Count: 3
File: inspect.lua, Line: 132, Error: inner loop in root trace, Count: 2
File: progress.lua, Line: 149, Error: leaving loop in root trace, Count: 2
File: files.lua, Line: 908, Error: leaving loop in root trace, Count: 2
File: language.lua, Line: 68, Error: NYI: bytecode FNEW  , Count: 2
File: await.lua, Line: 185, Error: NYI: bytecode FNEW  , Count: 2
File: 0x01d108c59670, Line: 7, Error: leaving loop in root trace, Count: 2
File: luadoc.lua, Line: 582, Error: NYI: bytecode UCLO  , Count: 2
File: files.lua, Line: 169, Error: leaving loop in root trace, Count: 2
File: global.lua, Line: 735, Error: leaving loop in root trace, Count: 2
File: compile.lua, Line: 4084, Error: NYI: bytecode FNEW  , Count: 2
File: luadoc.lua, Line: 583, Error: NYI: bytecode UCLO  , Count: 2
File: luadoc.lua, Line: 590, Error: NYI: bytecode UCLO  , Count: 2
File: guide.lua, Line: 775, Error: leaving loop in root trace, Count: 2
File: loading.lua, Line: 114, Error: NYI: bytecode FNEW  , Count: 2
File: gitignore.lua, Line: 95, Error: error thrown or hook called during recording, Count: 2
File: compile.lua, Line: 3902, Error: loop unroll limit reached, Count: 2
File: variable.lua, Line: 220, Error: leaving loop in root trace, Count: 2
File: luadoc.lua, Line: 2011, Error: inner loop in root trace, Count: 2
File: guide.lua, Line: 523, Error: NYI: bytecode FNEW  , Count: 2
File: utility.lua, Line: 727, Error: NYI: bytecode UCLO  , Count: 2
File: compile.lua, Line: 3632, Error: leaving loop in root trace, Count: 2
File: luadoc.lua, Line: 1854, Error: loop unroll limit reached, Count: 2
File: compile.lua, Line: 2764, Error: inner loop in root trace, Count: 2
File: 0x01d1082d30b0, Line: 8, Error: leaving loop in root trace, Count: 2
File: 0x01d108f354e0, Line: 9, Error: leaving loop in root trace, Count: 2
File: 0x01d109b0b9d0, Line: 4, Error: leaving loop in root trace, Count: 2
File: luadoc.lua, Line: 2135, Error: NYI: bytecode FNEW  , Count: 2
File: guide.lua, Line: 541, Error: NYI: bytecode UCLO  , Count: 2
File: guide.lua, Line: 540, Error: leaving loop in root trace, Count: 2
File: await.lua, Line: 152, Error: NYI: bytecode VARG  , Count: 2
File: 0x01d10bece3a0, Line: 3, Error: inner loop in root trace, Count: 2
File: compile.lua, Line: 2199, Error: inner loop in root trace, Count: 2
File: plugin.lua, Line: 51, Error: leaving loop in root trace, Count: 2
File: global.lua, Line: 710, Error: NYI: bytecode FNEW  , Count: 2
File: 0x01d1090b8dc0, Line: 3, Error: inner loop in root trace, Count: 2
File: luadoc.lua, Line: 2251, Error: leaving loop in root trace, Count: 2
File: 0x01d1086d96a0, Line: 4, Error: inner loop in root trace, Count: 2
File: 0x01d108f351b0, Line: 6, Error: leaving loop in root trace, Count: 2
File: luadoc.lua, Line: 2186, Error: NYI: bytecode FNEW  , Count: 2
File: capability.lua, Line: 18, Error: inner loop in root trace, Count: 1
File: capability.lua, Line: 42, Error: error thrown or hook called during recording, Count: 1
File: capability.lua, Line: 46, Error: leaving loop in root trace, Count: 1
File: inspect.lua, Line: 274, Error: inner loop in root trace, Count: 1
File: config.lua, Line: 209, Error: inner loop in root trace, Count: 1
File: template.lua, Line: 179, Error: inner loop in root trace, Count: 1
File: workspace.lua, Line: 34, Error: leaving loop in root trace, Count: 1
File: log.lua, Line: 59, Error: inner loop in root trace, Count: 1
File: compile.lua, Line: 3195, Error: leaving loop in root trace, Count: 1
File: luadoc.lua, Line: 2021, Error: inner loop in root trace, Count: 1
File: 0x01d108503440, Line: 16, Error: leaving loop in root trace, Count: 1
File: guide.lua, Line: 813, Error: leaving loop in root trace, Count: 1
File: guide.lua, Line: 807, Error: leaving loop in root trace, Count: 1
File: utility.lua, Line: 848, Error: NYI: bytecode FNEW  , Count: 1
File: node.lua, Line: 67, Error: inner loop in root trace, Count: 1
File: luadoc.lua, Line: 1795, Error: loop unroll limit reached, Count: 1
File: guide.lua, Line: 204, Error: inner loop in root trace, Count: 1
File: timer.lua, Line: 96, Error: leaving loop in root trace, Count: 1
File: pub.lua, Line: 203, Error: error thrown or hook called during recording, Count: 1
File: loading.lua, Line: 31, Error: error thrown or hook called during recording, Count: 1
File: config.lua, Line: 189, Error: error thrown or hook called during recording, Count: 1
File: config.lua, Line: 161, Error: error thrown or hook called during recording, Count: 1
File: gitignore.lua, Line: 176, Error: error thrown or hook called during recording, Count: 1
File: service.lua, Line: 158, Error: error thrown or hook called during recording, Count: 1
File: workspace.lua, Line: 91, Error: error thrown or hook called during recording, Count: 1
File: files.lua, Line: 819, Error: error thrown or hook called during recording, Count: 1
File: loading.lua, Line: 70, Error: error thrown or hook called during recording, Count: 1
File: timer.lua, Line: 228, Error: error thrown or hook called during recording, Count: 1
File: config.lua, Line: 34, Error: error thrown or hook called during recording, Count: 1
File: workspace.lua, Line: 345, Error: error thrown or hook called during recording, Count: 1
File: guide.lua, Line: 776, Error: loop unroll limit reached, Count: 1
File: json.lua, Line: 179, Error: inner loop in root trace, Count: 1
File: utility.lua, Line: 220, Error: leaving loop in root trace, Count: 1
File: 0x01d1090b85a0, Line: 3, Error: loop unroll limit reached, Count: 1
File: progress.lua, Line: 143, Error: inner loop in root trace, Count: 1
File: 0x01d17e87c390, Line: 8, Error: leaving loop in root trace, Count: 1
File: luadoc.lua, Line: 1956, Error: inner loop in root trace, Count: 1
File: luadoc.lua, Line: 870, Error: loop unroll limit reached, Count: 1
File: compile.lua, Line: 2080, Error: loop unroll limit reached, Count: 1
File: 0x01d10bece3a0, Line: 3, Error: loop unroll limit reached, Count: 1
File: compile.lua, Line: 2568, Error: loop unroll limit reached, Count: 1
File: utility.lua, Line: 215, Error: leaving loop in root trace, Count: 1
File: compile.lua, Line: 2075, Error: loop unroll limit reached, Count: 1
File: 0x01d1090ab5a0, Line: 3, Error: inner loop in root trace, Count: 1
File: guide.lua, Line: 337, Error: loop unroll limit reached, Count: 1
File: compile.lua, Line: 4122, Error: leaving loop in root trace, Count: 1
File: scope.lua, Line: 87, Error: leaving loop in root trace, Count: 1
File: guide.lua, Line: 779, Error: inner loop in root trace, Count: 1
File: variable.lua, Line: 227, Error: leaving loop in root trace, Count: 1
File: 0x01d1086daf00, Line: 3, Error: inner loop in root trace, Count: 1
File: global.lua, Line: 187, Error: leaving loop in root trace, Count: 1
File: luadoc.lua, Line: 1656, Error: error thrown or hook called during recording, Count: 1
File: gitignore.lua, Line: 149, Error: leaving loop in root trace, Count: 1
File: 0x01d10becdec0, Line: 3, Error: loop unroll limit reached, Count: 1
File: compile.lua, Line: 1722, Error: down-recursion, restarting, Count: 1
File: compile.lua, Line: 2577, Error: loop unroll limit reached, Count: 1
File: compile.lua, Line: 1394, Error: error thrown or hook called during recording, Count: 1
File: compile.lua, Line: 571, Error: loop unroll limit reached, Count: 1
File: pub.lua, Line: 138, Error: NYI: bytecode FNEW  , Count: 1
File: files.lua, Line: 722, Error: NYI: bytecode FNEW  , Count: 1
File: luadoc.lua, Line: 1975, Error: loop unroll limit reached, Count: 1
File: compile.lua, Line: 4020, Error: inner loop in root trace, Count: 1
File: guide.lua, Line: 572, Error: inner loop in root trace, Count: 1
File: luadoc.lua, Line: 2138, Error: loop unroll limit reached, Count: 1
File: pub.lua, Line: 203, Error: inner loop in root trace, Count: 1

CppCXY avatar Oct 03 '24 13:10 CppCXY

Just to note that calls into C code cannot be JIT compiled, so if LuaLS calls into C a lot (into bee.lua), then it's likely the JIT isn't able to do much.

Are you sure about this? I am pretty sure that it can be JIT compiled, the C calls become inlined

Pretty sure. Calls into C can't be JIT compiled, but calls into C via the FFI can.

Read the top of: https://luajit.org/ext_ffi.html

The FFI library is tightly integrated into LuaJIT (it's not available as a separate module). The code generated by the JIT-compiler for accesses to C data structures from Lua code is on par with the code a C compiler would generate. Calls to C functions can be inlined in JIT-compiled code, unlike calls to functions bound via the classic Lua/C API.

bee.lua uses the "classic Lua/C API".

lewis6991 avatar Oct 03 '24 13:10 lewis6991

bee.lua uses the "classic Lua/C API".

a bit mistaken; the luajit porting work was done on my rust version, not bee.lua. The entire bee API was implemented by me using rust. However, the conclusion remains unchanged: it is indeed a C API call. From the jit trace data, it appears that the significant performance degradation is due to too many jit failures, rather than an issue with the C API itself.

CppCXY avatar Oct 03 '24 13:10 CppCXY

Ok, there is some space to rearrange the code a bit to work around the NYI (not yet implemented) bytecodes. The FNEW stands for closure construction. Maybe the most offending closures can be made free functions instead? Particularly https://github.com/LuaLS/lua-language-server/blob/f6dcbcc08d8d07e4a4bb61dfd3c9b367078818f1/script/parser/luadoc.lua#L896C20-L896C30pushResume

Resource: https://chrisfls.github.io/luajit-wiki/NYI/

C3pa avatar Oct 03 '24 14:10 C3pa

Resource: https://chrisfls.github.io/luajit-wiki/NYI/

Is this up-to-date?

tmillr avatar Oct 13 '24 20:10 tmillr

Just to note that calls into C code cannot be JIT compiled, so if LuaLS calls into C a lot (into bee.lua), then it's likely the JIT isn't able to do much.

Are you sure about this? I am pretty sure that it can be JIT compiled, the C calls become inlined

From http://luajit.org/ext_ffi.html:

The FFI library allows calling external C functions and using C data structures from pure Lua code.

The FFI library largely obviates the need to write tedious manual Lua/C bindings in C. No need to learn a separate binding language — it parses plain C declarations! These can be cut-n-pasted from C header files or reference manuals. It's up to the task of binding large libraries without the need for dealing with fragile binding generators.

The FFI library is tightly integrated into LuaJIT (it's not available as a separate module). The code generated by the JIT-compiler for accesses to C data structures from Lua code is on par with the code a C compiler would generate. Calls to C functions can be inlined in JIT-compiled code, unlike calls to functions bound via the classic Lua/C API.

So it appears that to achieve best performance, you need to use the FFI to call non-Lua/pre-compiled functions. In that case, when the Lua is jit-compiled, the external calls to pre-compiled code will often be inlined and it will all get compiled as one binary/native (function).

Idk the finer details, but if I'm not mistaken, with the usual/classic Lua C api you are forced to use the Lua calling convention and Lua stack (for passing args, returns, etc.)? But with the FFI, you simply call regular C functions directly (which are not written to use the Lua stack at all), which forgoes the Lua stack entirely and uses the native calling convention/stack? So (for jit-compiled code) you not only get inlined calls, but you get to avoid the Lua stack completely (which is probably implemented in the heap) including all of the code which manages it. But the hard part (probably?) is taking advantage of all this and getting the jit settings just right, etc., in order to maximize your application's performance and offset the overhead of dynamic/runtime jit-compilation. Maybe some data structures need to be converted over to use native structs and arrays as well? (which may be managed/used/handled directly in Lua via luajit's builtin FFI)

My understanding is that C calls are inlined in jit-compiled Lua if calling C functions via the FFI. For best performance however, I believe you need to write "native" C functions (i.e. written without Lua in mind, written without using Lua's stack/calling convention, and written using native data types/structures) as much as possible. So perhaps taking full advantage of all of this would require a decent amount of the codebase to be rewritten? OTOH, I thought that simply switching to luajit runtime alone would have been enough to improve performance, but maybe I'm wrong.

tmillr avatar Oct 13 '24 20:10 tmillr

Resource: https://chrisfls.github.io/luajit-wiki/NYI/

Is this up-to-date?

That's someone's backup of the last time this page was on LuaJIT's wiki, which the uploader got from archive.org. That's as up-to-date as it can be. LuaJIT's master branch has recently had some development, but IIRC none pertaining to implementing any remaining NYI bytecodes.

C3pa avatar Oct 14 '24 12:10 C3pa

bee.lua uses the "classic Lua/C API".

whoops i thought you meant using FFI

OTOH, I thought that simply switching to luajit runtime alone would have been enough to improve performance, but maybe I'm wrong.

Same, I think its due to the frequency of the C api calls, and if its inlined with FFI (or fully rewritten to only use FFI) it could be truly unleashed

Frityet avatar Oct 14 '24 17:10 Frityet

OTOH, I thought that simply switching to luajit runtime alone would have been enough to improve performance, but maybe I'm wrong.

Same, I think its due to the frequency of the C api calls, and if its inlined with FFI (or fully rewritten to only use FFI) it could be truly unleashed

From my benchmark above: https://github.com/LuaLS/lua-language-server/issues/2879#issuecomment-2385026553

  • luajit vm's raw performance (with jit.off()) seems faster than regular lua: 🤔
  • compile time of a large file is 40% faster
  • the benchmarked code region is just script/parser/compile.lua, which is written in pure lua without bee.lua or other C api calls
  • and since the server is run with jit.off(), I believe this performance gain is unrelated to frequency of c api calls / ffi => it's that the raw performance is faster

tomlau10 avatar Oct 15 '24 06:10 tomlau10