Reduce busy-waiting CPU load in waitEvents()#201
Conversation
37a1a81 to
781cc8f
Compare
The {@link #waitEvents()} implementation on non-windows systems
usually returns immediately. This is unfortunate for the callers
which want to await events in an infinite-loop. As doing so would
burn lot of CPU time.
For example: Code in `LinuxEventThread.run()` (in `SerialPort.java`)
calls us in an infinite-loop. As a work-around, it uses a (very) small
sleep, to not utilize a full CPU core all the time. But still, this
permanently wastes a lot of CPU cycles (that many, that it is a problem
in our production use-case). The win32 code uses `OVERLAPPED` structs
and `WaitSingleObject()` which already provide that kind of "wait"
mechanism.
Not perfect, but this patch at least provides a way to wait if nothing
is ready. That "feature" is off by default and can be enabled
individually.
Edit: TryFix CICD build
> - try fix error:
> "Compatibility with CMake < 3.5 has been removed from CMake."
> - Use an older ubuntu to broaden runtime compatiblity
> - try fix error:
> "Failed to locate 'make', requesting installation of command line developer tools"
> - Revert some non-working MacOS fixes
> - Explain why to use older ubuntu version
Squashed-From: b5072ca
781cc8f to
116784b
Compare
| jobs: | ||
| ubuntu: | ||
| runs-on: [ubuntu-latest] | ||
| # Should be kept as old as possible to have better compatibility to |
There was a problem hiding this comment.
AFAIR, we produce production binaries with dockcross in workflows/cross-compile.yml, so this can probably safely be reverted. Reference: #190
There was a problem hiding this comment.
Oh 🙂 yes. Looks like I was missing that.
|
|
||
| private int[][] waitEvents() { | ||
| return serialInterface.waitEvents(portHandle); | ||
| return serialInterface.waitEvents2(portHandle, waitEventsTimeoutMs); |
There was a problem hiding this comment.
Can you please explain the rationale behind keeping the old API intact? We don't really promise the ability to swap native binaries and from what I can tell, the old waitEvents becomes inaccessible now so what is the intent here? Is it for the edge-case someone swaps the binary or the ability to revert if this causes issue?
There was a problem hiding this comment.
TL:DR; Not strictly necessary, just preferrable for the project where I have to use jssc 🙈
Is it for the edge-case someone swaps the binary?
This is the closest fit 🙂 Unluckily the project I'm working on (which uses JSSC) has an overly complicated setup.
This project, not intentionally but more indirectly seems to depends on deploying the binary through another way than the java parts (agree, ugly). Due to way too complicated reasons. There's a way too long chain of technical details why this is the case 😔 So yes, the java part in unlucky circumstances can be a mismatching version compared to the shared objects 🙁
.class |
.so |
With Compatibility | Without | |
|---|---|---|---|---|
| old | old | works fine | works fine | |
| new | new | works fine | works fine | |
| old | new | works fine | Runtime Linking Error | <- here |
| new | old | Runtime Linking Error | Runtime Linking Error |
I'll throw that question in my team and we'll see what happens 🙂 Maybe we come up with an easlier migration strategy.
Edit: Hmm. I think I should verify again, if that "separate-distribution" thing really is true 🤔 Need to have a look with co-workers.
There was a problem hiding this comment.
Since the code change in this PR is essentially native-only, I feel like one could objective just reduce the entropy by half by simply not touching the JAR at all but I'm probably misunderstanding something. I totally get the "tech-debt" stuff... I've moved all of my systems away from letting the JAR deploy the native lib per #92 so I'm 100% with you there, I just don't particularly care for namespacing obsolete code that's never to be used again. I'm probably misunderstanding something but I feel like you can:
- Keep the existing native signature; deploy the native lib without touching the jar/class files
- Eventually deploy the jar/class files
... assuming your versioning of the two must be independent. Sorry if I'm missing something or minimizing your struggles. The project is very grateful to have you help with the C++ portions, so we can certainly tackle this as you wish and add @Deprecated to clean it up later, I probably just misunderstand the use-case.
There was a problem hiding this comment.
We had another look onto our situation. Looks like parts of my assumption were wrong.
This means, we should be safe to get rid of the backwards compatibility stuff 🥳
I'll give it a try 👍
The issue seems to be with the older SDK version the pipeline manually downloads and forces:
We can fix the build by either using older macos runner ( |
@pietrygamat thanks for digging into this... I have to admit each time I revisit this my memory is more and more foggy what we can and can't do with the older SDKs. I bump into this a lot with another project and this is what we do over there... macos:
strategy:
fail-fast: false
matrix:
arch: [ x86_64, arm64 ]
include:
- arch: x86_64
os: macos-15-intel
xcode: "16.4"
- arch: arm64
os: macos-15
xcode: "16.4"... specifically though, we leverage an environment variable
That's a lot to say that Copying the other project's strategy it does still use a hard-coded runner version Edit: Whoops, sorry for the copy/paste tags above. |
I did not yet understand the comments about MacOS. So what should jssc do? I'll give one of the suggestions a try 🙂 Edit 1: I could not find anything that would look like |
Your PR can just use an old runner, even if it's EOL in a month. @pietrygamat and I will have to look for a more permanent solution, but it doesn't necessarily need to be fixed here. |
The
waitEvents()implementation on non-windows systems usually returns immediately. This is unfortunate for the callers which want to await events in an infinite-loop. As doing so would burn lot of CPU time.For example: Code in
LinuxEventThread.run()(inSerialPort.java) calls us in an infinite-loop. As a work-around, it uses a (very) small sleep, to not utilize a full CPU core all the time. But still, this permanently wastes a lot of CPU cycles (that many, that it is a problem in our production use-case). The win32 code usesOVERLAPPEDstructs andWaitSingleObject()which already provide that kind of "wait" mechanism.Edit: Unfortunately I've no idea how to fix macOS CICD build :(