Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relatively high cpu utilization when idle #329

Closed
rwols opened this issue Dec 27, 2020 · 24 comments
Closed

Relatively high cpu utilization when idle #329

rwols opened this issue Dec 27, 2020 · 24 comments
Labels
enhancement New feature or request

Comments

@rwols
Copy link

rwols commented Dec 27, 2020

Describe the bug
This server uses about 10% of my CPU when doing nothing.

To Reproduce

  1. Start lua-language-server manually in a terminal. Something like this: ./bin/macOS/lua-language-server -E -e LANG=\"EN-us\" main.lua
  2. Watch CPU utilization float around 10% in an activity manager.

Expected behavior
0% CPU utilization when idle.

Screenshots
image

Environment (please complete the following information):

  • OS: macOS 11.1

Additional context

I'm guessing this is due to a timer busy-looping in script/service/service.lua:

function m.startTimer()
    while true do
        ::CONTINUE::
        pub.step()
        if await.step() then
            goto CONTINUE
        end
        thread.sleep(0.001)
        timer.update()
    end
end

But it's only speculation at this point.

@sumneko
Copy link
Collaborator

sumneko commented Dec 28, 2020

Set Lua.diagnostics.workspaceDelay to -1, then check if it is 0% CPU utilization when idle.

@tsafin
Copy link

tsafin commented Jan 4, 2021

Is it possible to make -1 be safe default for Lua.diagnostics.workspaceDelay?

@gegoune
Copy link

gegoune commented Jan 4, 2021

Interesting, it did not work for me.

@sumneko
Copy link
Collaborator

sumneko commented Jan 5, 2021

What is 10% mean in your manager?
In some platform like Windows, 10% means it takes 10% usage of all CPUs, so 10% means that it uses all one logical CPU (if the computer has 8 logical CPUs).
In some other platforms, 10% means it takes 10% usage of one CPU.

If this is the first case, it shows that the extension is not really idle, and the common situation is that it is carrying out workspace diagnosis. You can adjust the delay and running rate of workspace diagnostics by setting, or disable this feature directly.

If this is the second case, it shows that while true do thread.sleep(0.001) end consumption on some machines is much higher than I expected. On my machine (Windows), it always consumes 0%.

@rwols
Copy link
Author

rwols commented Jan 5, 2021

What is 10% mean in your manager?

X% in Activity Monitor means X% of one virtual core. I have an Intel i7 with 4 cores, with hyper threading, so the percentage can go up to 800%.

the common situation is that it is carrying out workspace diagnosis.

But why do we need this? Can't this language server only act once it receives a message on the stdin pipe?

@sumneko
Copy link
Collaborator

sumneko commented Jan 5, 2021

But why do we need this? Can't this language server only act once it receives a message on the stdin pipe?

If you deleted a global variable definition in one file, it will check if other files are still using this global variable.
If you dont like this, you could disable it by setting.

@rwols
Copy link
Author

rwols commented Jan 6, 2021

If you deleted a global variable definition in one file, it will check if other files are still using this global variable.

Sure, but this check is only required once you receive a textDocument/didChange or a change via workspace/didChangeWatchedFiles right? Why do we need to check this a thousand times every second?

@sumneko
Copy link
Collaborator

sumneko commented Jan 6, 2021

Sure, but this check is only required once you receive a textDocument/didChange or a change via workspace/didChangeWatchedFiles right? Why do we need to check this a thousand times every second?

Yes, however, sometimes it will take more time to complete the diagnosis, depending on the number and size of your files.
Taking this project as an example, it takes 2.2 seconds to make a workspace diagnosis.
In another project with 2900 files, the number is 30 seconds.

@sumneko
Copy link
Collaborator

sumneko commented Jan 6, 2021

X% in Activity Monitor means X% of one virtual core. I have an Intel i7 with 4 cores, with hyper threading, so the percentage can go up to 800%.

It seems while true do thread.sleep(0.001) end dose not be shown as 0% in your OS.
Please try to change it to 0.005 or 0.01, and tell me the result.

ur4ltz added a commit to ur4ltz/nvim-old that referenced this issue Jan 18, 2021
@gegoune
Copy link

gegoune commented Feb 2, 2021

I have just tried to lower https://github.com/sumneko/lua-language-server/blob//script/service/service.lua#L152 to 0.1. It went down from around 10-12% to ~5-6%. On 1.14.1.

@sumneko
Copy link
Collaborator

sumneko commented Feb 3, 2021

I have just tried to lower https://github.com/sumneko/lua-language-server/blob//script/service/service.lua#L152 to 0.1. It went down from around 10-12% to ~5-6%. On 1.14.1.

Please also try script/brave/work.lua:18 and script/brave/work.lua:49

@gegoune
Copy link

gegoune commented Feb 3, 2021

With

diff --git a/3rd/luamake b/3rd/luamake
--- a/3rd/luamake
+++ b/3rd/luamake
@@ -1 +1 @@
-Subproject commit a035c027761c004a8fc7837a43b0c9ec9b0ca8f1
+Subproject commit a035c027761c004a8fc7837a43b0c9ec9b0ca8f1-dirty
diff --git a/script/brave/brave.lua b/script/brave/brave.lua
index 08909074..41244b45 100644
--- a/script/brave/brave.lua
+++ b/script/brave/brave.lua
@@ -46,7 +46,7 @@ function m.start()
         local suc, name, id, params = m.taskpad:pop()
         if not suc then
             -- 找不到工作的勇者,只好睡觉
-            thread.sleep(0.001)
+            thread.sleep(0.1)
             goto CONTINUE
         end
         local ability = m.ability[name]
diff --git a/script/brave/work.lua b/script/brave/work.lua
index 5ec8178f..17bef877 100644
--- a/script/brave/work.lua
+++ b/script/brave/work.lua
@@ -15,7 +15,7 @@ brave.on('loadProto', function ()
             return
         end
         brave.push('proto', proto)
-        thread.sleep(0.001)
+        thread.sleep(0.1)
     end
 end)

diff --git a/script/service/service.lua b/script/service/service.lua
index 8f76a294..505376a7 100644
--- a/script/service/service.lua
+++ b/script/service/service.lua
@@ -149,7 +149,7 @@ function m.startTimer()
             m.working = false
             m.idleClock = os.clock()
         end
-        thread.sleep(0.001)
+        thread.sleep(0.1)
         ::CONTINUE::
         timer.update()
     end

idle cpu usage goes down to 0.2%.

@sumneko
Copy link
Collaborator

sumneko commented Feb 3, 2021

how about 0.01 ?

@gegoune
Copy link

gegoune commented Feb 3, 2021

1.7%

@sumneko
Copy link
Collaborator

sumneko commented Feb 3, 2021

I changed it to 0.01 seconds. On my own computer test, there is almost no difference between 0.001 and 0.01, but 0.1 will have a significant sense of delay.

@sumneko
Copy link
Collaborator

sumneko commented Feb 3, 2021

Could you please help me to do another test?
Run BIN/lua-language-server with following script: local thread = require 'bee.thread' while true do thread.sleep(0.001) end
This shows 0% CPU usage in my computer(Windows 10).
I'd like to make sure that your CPU usage problem is caused by frequent sleep or other hidden problems.

@gegoune
Copy link

gegoune commented Feb 3, 2021

Absolutely, but it still gives me 1.7% on idle.

sumneko added a commit that referenced this issue Feb 3, 2021
@sumneko
Copy link
Collaborator

sumneko commented Feb 3, 2021

Thank you!
It seems that thread sleep has high overhead in your OS, but low overhead in Windows (or Windows displays these overheads as 0% CPU usage).
b57cd42 Please try to apply this modification, I changed the sleep of the child thread to blocking.

@gegoune
Copy link

gegoune commented Feb 3, 2021

b57cd42 drops CPU usage to 0.5% while idling. Huge improvement!

@rwols
Copy link
Author

rwols commented Feb 4, 2021

I still think you should remove the sleep loops entirely and rely only on events from the client. That will guarantee you 0% idle cpu usage.

@sumneko
Copy link
Collaborator

sumneko commented Feb 5, 2021

I still think you should remove the sleep loops entirely and rely only on events from the client. That will guarantee you 0% idle cpu usage.

This is difficult to achieve.

  1. There are many implementations that rely on timers, such as telemetry and delayed workspace diagnosis.
  2. I need to read the client's protocol in the child thread and send it to the worker thread through the channel, and the worker thread checks whether there is a new protocol through a round-robin method. This design is to be able to respond to automatic completion requests as soon as possible in unimportant tasks (such as diagnosis).

@sumneko
Copy link
Collaborator

sumneko commented Feb 5, 2021

This is the current working mode:
image

May be I can change it to:
image

@sumneko
Copy link
Collaborator

sumneko commented Mar 15, 2021

Done

@sumneko sumneko closed this as completed Mar 15, 2021
@rwols
Copy link
Author

rwols commented Mar 22, 2021

Yep, CPU utilization looks much better now. Apologies for not responding earlier. I've updated my sublime package to download 1.19.0 of the server sublimelsp/LSP-lua@a522abf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants