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

Multi-threaded access to index.add() runs into a deadlock (Swift) #354

Open
2 tasks done
spicymatt opened this issue Feb 25, 2024 · 5 comments
Open
2 tasks done

Multi-threaded access to index.add() runs into a deadlock (Swift) #354

spicymatt opened this issue Feb 25, 2024 · 5 comments
Labels
bug Something isn't working

Comments

@spicymatt
Copy link

Describe the bug

When calling USearchIndex.add() from multiple threads in the Swift concurrency (Swift) a deadlock situation arises in this code:

inline node_lock_t node_lock_(std::size_t slot) const noexcept

Threads are stuck in this loop and never return

Steps to reproduce

Use a TaskGroup like in the following example to add the vectors to the array concurrently:

public protocol USearchSignature:Sendable {
var us_id:UInt64 {get}
var us_vector:[Float] {get}
}

var index = USearchIndex
...
// used to control how concurrent we are
let numberOfConcurentTask = 5

// vectors is an array of USearchSignature
public add (vectors:[USearchSignature]) async throws {

var iterator = vectors.makeIterator()

// async code here adds the content in a concurrent manner into the index
for _ in 0..<numberOfConcurentTask {
if let current = iterator.next() {
try Task.checkCancellation()
group.addTask{
index.add(key: current.us_id, vector: current.us_vector)
}
}
}
for try await _ in group {
if let current = iterator.next() {
try Task.checkCancellation()
group.addTask{
index.add(key: current.us_id, vector: current.us_vector)
}
}
}
}

Expected behavior

No deadlock

uSearch is supposed to be thread safe for addition of vectors. It is crucial for us to be able to concurrently add vectors to the index when the system launches since the index building is too slow in single threaded mode. We use a saved index whenever we can...but sometimes we need to reload a 100k vector index (512 floats per vector) efficiently. Multi-threaded loading gives us decent performance. But we cannot afford such deadlocks and we do get them in the current code.

USearch version

v2.9

Operating System

macOS Sonoma

Hardware architecture

Arm

Which interface are you using?

Other bindings

Contact Details

[email protected]

Is there an existing issue for this?

  • I have searched the existing issues

Code of Conduct

  • I agree to follow this project's Code of Conduct
@spicymatt spicymatt added the bug Something isn't working label Feb 25, 2024
@ashvardanian
Copy link
Contributor

Interesting! Do you have a dataset example? I will try to reproduce.

@spicymatt
Copy link
Author

The dataset does not seem to matter. Will try to reproduce it in a sample but here is an example of a blocked code. I have 3 threads waiting for each other in

    return __atomic_fetch_or(&slots_[i / bits_per_slot()], mask, __ATOMIC_ACQUIRE) & mask;

of method: inline bool atomic_set(std::size_t i) noexcept {...}

Screenshot 2024-02-26 at 09 38 32

@spicymatt
Copy link
Author

@ashvardanian is this something you can reproduce on your side ? Regards.

@ashvardanian
Copy link
Contributor

Hi @spicymatt! I think I can, but I was working on the C# issues yesterday, and will try to find time to work on Swift today. It would be great if you could open a PR with a minimal test that breaks. It will definitely make things faster for me. Thanks!

@spicymatt
Copy link
Author

spicymatt commented Feb 27, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants