Buffers.jl Usage Guide
This guide provides detailed instructions and examples on how to use the Buffers.jl package.
Creating a Buffer
To create a buffer, you can use the Buffer
type. The buffer is automatically resized based on the elements added to it.
using Buffers
buffer = Buffer{Int}() # Create a buffer of type Int
You can also specify the initial size of the buffer (number of elements) to avoid reallocation.
buffer = Buffer(100) # Create a buffer with an initial size of 100 elements
Creating a manually allocated Buffer with MAllocBuffer
The MAllocBuffer
type is used to create a buffer with manually allocated memory. The buffer is not extended automatically, and you have to free the memory manually. It is highly recommended to use @buffer
macro instead of directly using MAllocBuffer
to avoid memory leaks. The advantage of using MAllocBuffer
is that the allocated arrays are simple Array
objects, which can be passed to C functions without any issues, i.e., the neuralyze
function is not needed.
[!WARNING] After freeing the memory, the allocated arrays are no longer valid and should not be used.
buffer = MAllocBuffer(1000) # Create a manually allocated buffer of type Float64
...
free!(buffer) # Free the memory, the buffer and allocated arrays are no longer valid
Creating a ThreadsBuffer
The ThreadsBuffer
type extends the functionality of Buffer
to support multi-threaded environments. It provides thread-safe operations for concurrent data manipulation.
tbuffer = ThreadsBuffer(1000) # Create a ThreadsBuffer of type Float64
All operations on Buffer
can be performed on ThreadsBuffer
as well.
The buffers in ThreadsBuffer
are thread-local, meaning each task uses its own buffer. This allows for concurrent allocation and deallocation of memory without causing data corruption. After the task is done, the buffer has to be released with reset!
or Buffers.release!
to be reused by another task.
ThreadsMAllocBuffer
is the manually allocated version of ThreadsBuffer
, and it is highly recommended to use the @threadsbuffer
macro instead of directly using ThreadsMAllocBuffer
to avoid memory leaks.
@threadsbuffer buf(1000) begin # Create a manually allocated ThreadsBuffer of type Float64
...
end
@buffer and @threadsbuffer macros
The @buffer
and @threadsbuffer
macros are used to create a manually allocated buffer with the specified size and type. The buffer is automatically freed when the scope of the macro ends.
@buffer buf(Int, 1000) begin # Create a manually allocated buffer of type Int with 1000 elements
A = alloc!(buf, 10, 10)
...
end
Two buffers can be created in the same scope, and they are automatically freed when the scope ends.
@buffer buf1(Int, 1000) buf2(Float64, 2000) begin
A = alloc!(buf1, 10, 10)
B = alloc!(buf2, 20, 5)
...
end
The @threadsbuffer
macro is used to create a manually allocated buffer in a threaded environment. The buffer is automatically released when the scope of the macro ends.
@threadsbuffer tbuf(Float64, 1000) begin
@sync for i in 1:4
Threads.@spawn begin
A = alloc!(tbuf, 10, 10)
...
drop!(tbuf, A)
reset!(tbuf) # Release the buffer for the current thread
end
end
...
end
Allocation with alloc!
The alloc!
function is used to allocate memory for tensors in the buffer.
using Buffers
buffer = Buffer(1000)
A = alloc!(buffer, 10, 10) # Allocate a 10x10 tensor
B = alloc!(buffer, 20, 5) # Allocate a 20x5 tensor
Deallocation with drop!
The drop!
function is used to deallocate memory for tensors from the buffer.
drop!(buffer, A) # Deallocate tensor A
drop!(buffer, B) # Deallocate tensor B
Resetting the Buffer with reset!
The reset!
function is used to reset the buffer to its initial state, clearing all allocated memory.
reset!(buffer) # Reset the buffer
Releasing the Buffer with Buffers.release!
For ThreadsBuffer
and ThreadsMAllocBuffer
, the Buffers.release!
function is used to release the buffer of the current thread. Note: the reset!
function includes a call to release!
.
tbuffer = ThreadsBuffer(1000)
Threads.@threads for i in 1:4
A = alloc!(tbuffer, 10, 10)
drop!(tbuffer, A)
Buffers.release!(tbuffer) # Release the buffer for the current thread
end
Reshaping the Buffer with reshape_buf!
The reshape_buf!
function is used to reshape the buffer to a new set of dimensions without copying the data or explicitly allocating a tensor on the buffer.
buffer = Buffer(1000)
A = reshape_buf!(buffer, 10, 10) # Reshape buffer to a 10x10 tensor
B = reshape_buf!(buffer, 20, 5, offset=100) # Reshape buffer to a 20x5 tensor starting at offset 100
Neuralyzing Tensors with neuralyze
The neuralyze
function is used to wipe the memory about the origin of a tensor. This can be helpful when you need to bypass Julia's aliasing checks or ensure that a tensor is treated as an independent array for example in a C call. This function is not needed when using MAllocBuffer
or ThreadsMAllocBuffer
.
A = alloc!(buffer, 10, 10)
An = neuralyze(A) # Neuralyze tensor A
Complete Example
Here is a complete example demonstrating the usage of alloc!
, drop!
, reset!
, and reshape_buf!
functions:
using Buffers
# Create a buffer
buffer = Buffer(1000)
# Allocate tensors
A = alloc!(buffer, 10, 10)
B = alloc!(buffer, 20, 5)
# Perform operations on tensors
rand!(A)
rand!(B)
# Deallocate tensors
drop!(buffer, A)
drop!(buffer, B)
# Create a ThreadsMAllocBuffer
@threadsbuffer tbuffer(1000) begin
# Use ThreadsMAllocBuffer in a threaded loop
@sync for i in 1:4
Threads.@spawn begin
A = alloc!(tbuffer, 10, 10)
B = alloc!(tbuffer, 20, 5)
drop!(tbuffer, A, B)
reset!(tbuffer) # Release the buffer for the current thread
end
end
end
# Reshape the buffer
C = reshape_buf!(buffer, 10, 10)
D = reshape_buf!(buffer, 20, 5, offset=100)
For more detailed information, please refer to the other sections of the documentation.