Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@guizmaii
Copy link
Member

@guizmaii guizmaii commented Jan 12, 2026

Fixes #10377

This PR does not fix the performance problem explained in #9093
To fix this problem, the Semaphore algorithm needs to be completely changed (See #10348)

The goal of this PR is to fix the current implementation, which is catastrophic in terms of memory/allocations when a Semaphore is under heavy load

See https://x.com/guizmaii/status/2011286658822656217?s=20 for real world allocation flamegraph showing the issue

Related PR:

* Returns the number of available permits and the number of tasks currently
* waiting for permits.
*/
def stats(implicit trace: Trace): UIO[Semaphore.Stats]
Copy link
Member Author

@guizmaii guizmaii Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a new little helper to get both available and awaiting in 1 call

ref.modify {
case Left(queue) =>
queue
.find(_._1 == promise)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The culprit for #10377 was most probably this .find call

Also, we were traversing the queue twice, here and below with the queue.filter

Comment on lines 233 to 241
val iterator = queue.iterator
val others = List.newBuilder[Job]
others.sizeHint(queue.size - 1)
var foundJob: Job = null
while (iterator.hasNext) {
val next = iterator.next()
if (next.promise == promise) foundJob = next
else others += next
}
Copy link
Member Author

@guizmaii guizmaii Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, I'm working on a second PR to optimise this. This is terrible for performances

Edit: See #10382

…10382)

* Optimise `Semaphore`: Use an optimised data structure to queue jobs

* Optimise `releaseN` loop

* fmt

* Add test

* Clean tests

* Add more tests

* fmt

* Optimise `Semaphore`: Remove allocation of `Promise` when not needed (#10383)
@kyri-petrou
Copy link
Contributor

It looks like we have a few PRs open for optimizing Semaphore at this stage, and I'd rather get just one of them merged in if it solves this issue + improves the performance of Semaphore as in the issue you linked.

I updated #9662 to fix the merge issues, would you be able to publish that branch and test it the same way you did for the changes in this PR and let me know whether it performs better / worse / about the same in terms of allocations?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Perfs] The runloop seems to generate a lot of scala.collection.immutable.List::reverse calls, which seems to be costly in allocations

3 participants