Skip to content

idea for doing synchronous evals but _not_ holding evalLock - #474

Open
davidkoski wants to merge 6 commits into
mainfrom
multithreaded-eval
Open

davidkoski wants to merge 6 commits into
mainfrom
multithreaded-eval

Conversation

@davidkoski

Copy link
Copy Markdown
Member

Proposed changes

  • do the synchronous mutation under lock with asyncEval
  • synchronous eval then does not need lock

Checklist

Put an x in the boxes that apply.

  • I have read the CONTRIBUTING document
  • I have run pre-commit run --all-files to format my code / installed pre-commit prior to committing changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the necessary documentation (if needed)

Comment thread Source/MLX/Transforms+Eval.swift Outdated
Comment on lines +125 to +142
mlx_eval(vector_array)
mlx_async_eval(vector_array)
}
mlx_eval(vector_array)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is the heart of the change. we need the evalLock to guard against mutation of shared state. python uses thread-local streams to scope this, but we can't do that on the swift side (at least not in the same way).

But what if we did the mutation part under lock and then just wait for the arrays to be ready outside the lock? The MLXArray instances are still not thread safe, so they can't escape to other threads where the mutation could be observed, so I think this is safe.

See #472 and #62

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

FYI @aleroot -- I got this idea from the concurrent read work you did and this might be generally useful.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice work.


@testable import MLX

// MARK: - Support

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For now this is just some tests that I had claude throw together.

@davidkoski davidkoski mentioned this pull request Sep 15, 2026
1 task done
@davidkoski davidkoski mentioned this pull request Sep 28, 2026
7 tasks
@davidkoski
davidkoski changed the base branch from main to fix-lock-inversion October 9, 2026 21:35
```swift
let g = grad { (x: MLXArray) -> MLXArray in
let y = x * x
print(y.asArray(Float.self)) // OK: synchronous eval of an intermediate

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Clarify that this case is explicitly allowed -- not all transforms prohibit evaluation:

Comment on lines +217 to +218
@Suite("eval inside function transformations")
struct EvalInTransformsTests {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Based on PR feedback this tests and asserts the different evaluations inside of transforms that are allowed vs not.

Comment thread Source/MLX/Export.swift
Comment on lines -148 to +150
mlx_export_function_kwargs(url.path, closure, positionalArgs, kwargs, shapeless)
withEvalLock {
mlx_export_function_kwargs(url.path, closure, positionalArgs, kwargs, shapeless)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

While investigating #499 found these spots that also require the evalLock

Comment on lines +190 to +202
let waitOutsideLock = withEvalLockState { state in
switch state {
case .nested:
// cannot release the lock to wait, and async eval rejects
// vjp/jvp tracers
_ = mlx_eval(vectorArray)
return false

case .outermost:
// schedule under the lock, wait (below) outside it
return mlx_async_eval(vectorArray) == 0
}
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This handling fixes the issues found in review, see also EvalInTransformsTests

@nwh nwh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍

Base automatically changed from fix-lock-inversion to main October 9, 2026 22:51

This branch has not been deployed

No deployments
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.

3 participants