Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/api/array.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ MtlVecOrMat

## Storage modes

The Metal API has various storage modes that dictate how a resource can be accessed. `MtlArray`s are `Metal.PrivateStorage` by default, but they can also be `Metal.SharedStorage` or `Metal.ManagedStorage`. For more information on storage modes, see the official [Metal documentation](https://developer.apple.com/documentation/metal/resource_fundamentals/setting_resource_storage_modes).
The Metal API has various storage modes that dictate how a resource can be accessed. `MtlArray`s are `Metal.SharedStorage` by default — zero-copy on unified-memory (Apple Silicon) GPUs — but they can also be `Metal.PrivateStorage` or `Metal.ManagedStorage`. On a discrete GPU, set the `default_storage` preference to `"private"` for best performance. For more information on storage modes, see the official [Metal documentation](https://developer.apple.com/documentation/metal/resource_fundamentals/setting_resource_storage_modes).

```@docs
Metal.PrivateStorage
Expand Down
20 changes: 11 additions & 9 deletions src/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ end

`N`-dimensional Metal array with storage mode `S` and elements of type `T`.

`S` can be `Metal.PrivateStorage` (default), `Metal.SharedStorage`.
`S` can be `Metal.SharedStorage` (default) or `Metal.PrivateStorage`.

See the Array Programming section of the Metal.jl docs for more details.
"""
Expand Down Expand Up @@ -190,14 +190,16 @@ See also `VecOrMat`(@ref) for examples.
"""
const MtlVecOrMat{T,S} = Union{MtlVector{T,S},MtlMatrix{T,S}}

# default to private memory
const DefaultStorageMode = let str = @load_preference("default_storage", "private")
if str == "private"
PrivateStorage
elseif str == "shared"
# Default to shared storage: zero-copy on unified-memory (Apple Silicon) GPUs and
# valid on discrete GPUs too (`__init__` steers discrete-GPU users to `private`).
# Override with the `default_storage` preference.
const DefaultStorageMode = let str = @load_preference("default_storage", "shared")
if str == "shared"
SharedStorage
elseif str == "private"
PrivateStorage
else
error("unknown default storage mode: $default_storage")
error("unknown default storage mode: $str (expected \"shared\" or \"private\")")
end
end

Expand Down Expand Up @@ -475,9 +477,9 @@ function Adapt.adapt_storage(to::MtlArrayAdaptor{S}, xs::AbstractArray{T,N}) whe
end

"""
mtl(A; storage=Metal.PrivateStorage)
mtl(A; storage=Metal.SharedStorage)

`storage` can be `Metal.PrivateStorage` (default) or `Metal.SharedStorage`.
`storage` can be `Metal.SharedStorage` (default) or `Metal.PrivateStorage`.

Opinionated GPU array adaptor, which may alter the element type `T` of arrays:
* For `T<:AbstractFloat`, it makes a `MtlArray{Float32}` for performance and compatibility
Expand Down
13 changes: 13 additions & 0 deletions src/initialization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@ function __init__()
return
end

# The default `SharedStorage` is optimal on unified-memory (Apple Silicon) GPUs
# and works everywhere, but on a discrete GPU `PrivateStorage` is usually faster.
# Steer those (rare) users to the preference, unless they already chose a default.
if DefaultStorageMode == SharedStorage &&
load_preference(Metal, "default_storage", nothing) === nothing &&
functional() && !device().hasUnifiedMemory
@warn """Metal.jl defaults to `SharedStorage`, which is optimal on unified-memory
(Apple Silicon) GPUs. This GPU does not have unified memory, where
`PrivateStorage` is usually faster. To switch the default, run
using Preferences; set_preferences!(Metal, "default_storage" => "private")
and restart Julia.""" maxlog = 1
end

# ensure that operations executed by the REPL back-end finish before returning,
# because displaying values happens on a different task
if isdefined(Base, :active_repl_backend) && !isnothing(Base.active_repl_backend)
Expand Down
6 changes: 6 additions & 0 deletions test/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ end
@test Adapt.adapt(MtlMatrix{ComplexF32, Metal.SharedStorage}, [1 2;3 4]) isa MtlArray{ComplexF32, 2, Metal.SharedStorage}
@test Adapt.adapt(MtlArray{Float16}, Float64[1]) isa MtlArray{Float16}

# MtlArrays default to shared storage; explicit storage modes still resolve.
@test Metal.DefaultStorageMode === Metal.SharedStorage
@test Metal.is_shared(MtlArray{Int}(undef, 4))
@test Metal.is_shared(mtl([1.0f0, 2.0f0, 3.0f0]))
@test Metal.is_private(MtlArray{Int, 1, Metal.PrivateStorage}(undef, 4))

# Test a few explicitly unsupported types
@test_throws "MtlArray only supports element types that are stored inline" MtlArray(BigInt[1])
@test_throws "Metal does not support Float64 values" MtlArray(Float64[1])
Expand Down
Loading