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

Add Poplar.get_ipu_model to more easily obtain an IPU Model #59

Merged
merged 1 commit into from
Apr 8, 2024
Merged
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
15 changes: 15 additions & 0 deletions docs/src/poplar.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ You can slice a tensor with the usual Julia notation `tensor[index1:index2]`, th

[`similar`](@ref) can be used to add to `graph` a tensor with the same shape and optionally element type as `tensor`, while [`copyto!`](@ref) can be used to copy elements of a CPU host array into an IPU tensor.

## Using `IPUToolkit.jl` without an IPU

While this package requires a physical IPU to use all the available features, you can still experiment with the IPU programming model even if you do not have access to a hardware IPU.
The Poplar SDK provides a feature called IPU Model, which is a software emulation of the behaviour of the IPU hardware.
While the IPU model comes with [some limitations](https://docs.graphcore.ai/projects/poplar-user-guide/en/latest/poplar_programs.html#programming-with-poplar), it can be useful for testing or debugging.

To use the IPU model in `IPUToolkit.jl`, define the device of your IPU program with `Poplar.IPUModelCreateDevice` (which calls [`IPUModel::createDevice`](https://docs.graphcore.ai/projects/poplar-api/en/3.4.0/poplar/profiling/IPUModel.html#_CPPv4NK6poplar8IPUModel12createDeviceE11OptionFlagsbj) under the hood):
```julia
device = Poplar.get_ipu_model()
# Then the rest of the program continues as usual
target = Poplar.DeviceGetTarget(device)
graph = Poplar.Graph(target)
# ...
```

```@autodocs
Modules = [IPUToolkit.Poplar]
```
6 changes: 1 addition & 5 deletions examples/main.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
using IPUToolkit.IPUCompiler
using IPUToolkit.Poplar

device = if Poplar.SDK_VERSION < v"2.0"
Poplar.IPUModelCreateDevice(Poplar.IPUModel())
else
Poplar.get_ipu_device()
end
device = Poplar.get_ipu_device()

target = Poplar.DeviceGetTarget(device)
graph = Poplar.Graph(target)
Expand Down
3 changes: 1 addition & 2 deletions examples/tutorial1.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using IPUToolkit.Poplar

# model = Poplar.IPUModel()
# device = Poplar.IPUModelCreateDevice(model)
# device = Poplar.get_ipu_model()
device = Poplar.get_ipu_device()

target = Poplar.DeviceGetTarget(device)
Expand Down
3 changes: 1 addition & 2 deletions examples/tutorial2.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using IPUToolkit.Poplar

# model = Poplar.IPUModel()
# device = Poplar.IPUModelCreateDevice(model)
# device = Poplar.get_ipu_model()
device = Poplar.get_ipu_device()

target = Poplar.DeviceGetTarget(device)
Expand Down
41 changes: 34 additions & 7 deletions src/poplar.jl
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ const ATTACHED_DEVICES_LOCK = ReentrantLock()

# Be sure to quit all julia sessions which hold devices!!!
"""
Poplar.get_ipu_devices(n::Int, hint::Union{AbstractVector{<:Integer},Integer}=0)
Poplar.get_ipu_devices(n::Int, hint::Union{AbstractVector{<:Integer},Integer}=0) -> Vector{Poplar.DeviceAllocated}

Try to attach to `n` IPU devices, returns a vector of the pointers to the devices
successfully attached to. You can release them with `Poplar.DeviceDetach` (note that this
Expand All @@ -222,8 +222,8 @@ attach. It can have different types:
* if of type `AbstractVector`, try to attach to `n` devices from that list of
IDs.

See [`Poplar.get_ipu_device`](@ref) for requesting exactly one IPU device.
To release all devices previously attached with `Poplar.get_ipu_devices` or [`Poplar.get_ipu_device`](@ref) use [`Poplar.detach_devices`](@ref).
See [`Poplar.get_ipu_device`](@ref) for requesting exactly one IPU device, and [`Poplar.get_ipu_model`](@ref) for requesting an IPU Model.
To release all devices previously attached with `Poplar.get_ipu_devices`, [`Poplar.get_ipu_device`](@ref), or [`Poplar.get_ipu_model`](@ref) use [`Poplar.detach_devices`](@ref).
"""
function get_ipu_devices(n::Int, hint::Union{AbstractVector{<:Integer},Integer}=0)
lock(ATTACHED_DEVICES_LOCK) do
Expand Down Expand Up @@ -261,12 +261,16 @@ function get_ipu_devices(n::Int, hint::Union{AbstractVector{<:Integer},Integer}=
end

"""
Poplar.get_ipu_device(hint::Union{AbstractVector{<:Integer},Integer}=0)
Poplar.get_ipu_device(hint::Union{AbstractVector{<:Integer},Integer}=0) -> Poplar.DeviceAllocated

Similar to [`Poplar.get_ipu_devices`](@ref), but request exactly one IPU device. If it can attach
to a device, return that pointer only (not in a vector, like `get_ipu_devices`), otherwise
return `nothing`. You can release the device with `Poplar.DeviceDetach(device)`.
To release all devices previously attached with `Poplar.get_ipu_device` or [`Poplar.get_ipu_devices`](@ref) use [`Poplar.detach_devices`](@ref).
return `nothing`.

See [`Poplar.get_ipu_model`](@ref) for requesting an IPU Model.

You can release the device with `Poplar.DeviceDetach(device)`.
To release all devices previously attached with `Poplar.get_ipu_device`, [`Poplar.get_ipu_devices`](@ref), or [`Poplar.get_ipu_model`](@ref) use [`Poplar.detach_devices`](@ref).

The optional argument `hint` suggests to which device IDs to try and
attach. It can have different types:
Expand All @@ -283,10 +287,33 @@ function get_ipu_device(hint::Union{AbstractVector{<:Integer},Integer}=0)
return nothing
end

"""
Poplar.get_ipu_model(ipu_version::String="ipu2") -> Poplar.DeviceAllocated

Attach to an [IPU Model](https://docs.graphcore.ai/projects/poplar-user-guide/en/latest/poplar_programs.html#programming-with-poplar), and return the attached device.
This uses [`IPUModel::createDevice`](https://docs.graphcore.ai/projects/poplar-api/en/3.4.0/poplar/profiling/IPUModel.html#_CPPv4NK6poplar8IPUModel12createDeviceE11OptionFlagsbj) under the hood.

The optional positional argument `ipu_version::String`, `ipu2` by default`, represents the version of the IPU to emulate.
Valid values for `ipu_version` are `ipu1` and `ipu2` (for Mk1 and Mk2 IPU architectures respectively).

See [`Poplar.get_ipu_device`](@ref) and [`Poplar.get_ipu_devices`](@ref) for requesting one or mode hardware IPUs.

You can release the device with `Poplar.DeviceDetach(device)`.
To release all devices previously attached with `Poplar.get_ipu_model`, [`Poplar.get_ipu_device`](@ref) or [`Poplar.get_ipu_devices`](@ref) use [`Poplar.detach_devices`](@ref).
"""
function get_ipu_model(ipu_version::String="ipu2")
lock(ATTACHED_DEVICES_LOCK) do
model = Poplar.IPUModel(ipu_version)
device = Poplar.IPUModelCreateDevice(model)
push!(ATTACHED_DEVICES, device)
device
end
end

"""
Poplar.detach_devices() -> Nothing

Detach all devices previously attached in the current Julia session with [`Poplar.get_ipu_devices`](@ref) or [`Poplar.get_ipu_device`](@ref).
Detach all devices previously attached in the current Julia session with [`Poplar.get_ipu_devices`](@ref), [`Poplar.get_ipu_device`](@ref), or [`Poplar.get_ipu_model`](@ref).
"""
function detach_devices()
lock(ATTACHED_DEVICES_LOCK) do
Expand Down
3 changes: 1 addition & 2 deletions test/compiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,7 @@ end
match_mode=:any,
Poplar.get_ipu_device())
else
model = @cxxtest Poplar.IPUModel()
Poplar.IPUModelCreateDevice(model)
Poplar.get_ipu_model()
end

# Run a test program
Expand Down
3 changes: 1 addition & 2 deletions test/poplar.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,7 @@ end

# Test a simple program using a software-emulated IPU (IPU model)
@testset "IPU Model" begin
model = @cxxtest Poplar.IPUModel()
device = @cxxtest Poplar.IPUModelCreateDevice(model)
device = @cxxtest Poplar.get_ipu_model()
test_poplar_program(device)
end

Expand Down
Loading