Skip to content

[BUG] Default device is gpu on cpu-only Linux. #395

@a1091150

Description

@a1091150

Describe the bug
On cpu-only Linux machine, I discover that mlx c++ uses cpu device, but mlx-swift uses gpu backend which is unavailable.

  • The method Device.setDefault is deprecated, but without it, the default device is still gpu.
  • The method Device.withDefaultDevice is not working on Linux.
  • MLXFast and others targets can not build on Linux.
  • Both main branch and 0.10.0 version have the bug.

To Reproduce

Include code snippet

  • Create a package with executable.
// swift-tools-version: 6.3

import PackageDescription

let package = Package(
    name: "mlx_remote_test",
    dependencies: [
        .package(url: "https://github.com/ml-explore/mlx-swift", from: "0.10.0")
    ],
    targets: [
        .executableTarget(
            name: "mlx_remote_test",
            dependencies: [
                .product(name: "MLX", package: "mlx-swift")
            ]
        )
    ],
    swiftLanguageModes: [.v6]
)
@main
struct mlx_remote_test {
    static func main() {
        Device.withDefaultDevice(.cpu, {
        let x = MLXArray.ones([2, 3])
        let y = MLXArray(10.0)
        let z = x + y
        let meanValue = z.mean()

        MLX.eval(z, meanValue)

        print("x shape: \(x.shape)")
        print("y: \(y)")
        print("z = x + y:\n\(z)")
        print("mean(z): \(meanValue)")
         })

    }
}

Expected behavior

  • mlx-c have some methods to check gpu or cpu.
  • Device.withDefaultDevice should change device.

Desktop:

  • OS Version: Linux 6.8.0-87-generic 88~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC x86_64 x86_64 x86_64 GNU/Linux
  • Device: A cheap Asus laptop without GPU.
  • Version: 6.8.0-87-generic

Additional context
One possible fix to change default device.

    private static func _resolveGlobalDefaultDevice() -> Device {
        _lock.withLock {
-           _defaultDevice ?? .gpu
+            if let device = _defaultDevice {
+                return device
+            }

+            var ctx = mlx_device_new()
+            mlx_get_default_device(&ctx)
+            return Device(ctx)
        }
    }

Recently I modified mlx-swift to expose MLX C++ by moving mlx C++ to a new target MLXCxx, maybe a good pull request?

Some related directories to move:

  • Move mlx c++ submodule to Source/MLXCxx
  • Source/Cmlx/fmt -> Source/MLXCxx/fmt
  • Source/Cmlx/json -> Source/MLXCxx/json
  • Source/Cmlx/metal-cpp -> Source/MLXCxx/metal-cpp
  • Source/Cmlx/mlx -> Source/MLXCxx/mlx
  • Source/Cmlx/mlx-generated -> Source/MLXCxx/mlx-generated
  • Source/Cmlx/mlx-conditional -> Source/MLXCxx/mlx-conditional

New Target:

let mlxcxx = Target.target(
    name: "MLXCxx",
    path: "Source/MLXCxx",
    exclude: platformExcludes + [...],
    publicHeadersPath: "mlx",
    cSettings: [
        .headerSearchPath("mlx"),
    ],
    cxxSettings: cxxSettings + [
        .headerSearchPath("mlx"),
        .headerSearchPath("json/single_include/nlohmann"),
        .headerSearchPath("fmt/include"),
        .define("SWIFTPM_BUNDLE", to: "\"mlx-swift_MLXCxx\""),
        .define("MLX_VERSION", to: "\"0.31.1\""),
    ],
    linkerSettings: linkerSettings
)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions