docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Rewindable allocator overview

    A rewindable allocator is a custom allocator that works in a similar way to a linear allocator. It's fast and thread safe. A rewindable allocator pre-allocates blocks of memory in advance.

    When you request memory from a rewindable allocator, it selects a range of memory from its pre-allocated block and assigns it to use. The minimum alignment of rewindable allocations is 64 bytes. After it uses all the existing blocks of memory, the rewindable allocator allocates another block of memory.

    It doubles the size of the new block until it reaches a maximum block size. When it reaches this point, the rewindable allocator adds the maximum block size to its previous block size to increase its block size linearly.

    One advantage of rewindable allocator is that you don't need to free individual allocations. As its name implies, a rewindable allocator can rewind and free all your allocations at one point.

    When you rewind an allocator, the allocator keeps the memory blocks that it used before to improve performance and disposes the rest of the blocks. When you request to free or dispose a memory allocation from a rewindable allocator, it's a no-op unless you set the enable block free flag of the rewindable allocator. When you set the flag to enable block free, the rewindable allocator rewinds a memory block when it frees the last allocation from the block.

    Declare and create a rewindable allocator

    To create a rewindable allocator, you must do the following:

    • Allocate memory to hold the rewindable allocator
    • Add an entry in the global allocator table to register the allocator
    • Pre-allocate the allocator's first memory block to initialize it.

    You can use the wrapper AllocatorHelper to create a rewindable allocator.

    The following example declares and creates a rewindable allocator:

    // Example user structure
    internal struct ExampleStruct
    {
        // Use AllocatorHelper to help creating a rewindable alloctor
        AllocatorHelper<RewindableAllocator> rwdAllocatorHelper;
    
        // Rewindable allocator property for accessibility
        public ref RewindableAllocator RwdAllocator => ref rwdAllocatorHelper.Allocator;
    
        // Create the rewindable allocator
        void CreateRewindableAllocator(AllocatorManager.AllocatorHandle backgroundAllocator, int initialBlockSize, bool enableBlockFree = false)
        {
            // Allocate the rewindable allocator from backgroundAllocator and register the allocator
            rwdAllocatorHelper = new AllocatorHelper<RewindableAllocator>(backgroundAllocator);
    
            // Allocate the first memory block with initialBlockSize in bytes, and indicate whether
            // to enable the rewindable allocator with individual block free through enableBlockFree
            RwdAllocator.Initialize(initialBlockSize, enableBlockFree);
        }
    }
    

    Use a rewindable allocator to allocate memory

    For Native- collection types, allocation from a rewindable allocator is similar to a classic allocator, except you must use CollectionHelper.CreateNativeArray to create a NativeArray from a rewindable allocator. When you use a rewindable allocator to create a Native- collection type, its safety handle is added to the list of child safety handles of the rewindable allocator.

    For Unsafe- collection types, you must use AllocatorManager.Allocate to allocate memory from a rewindable allocator.

    You don't need to dispose individual allocations. When all allocations aren't needed anymore, call the Rewind method of a rewindable allocator to free all its allocations. When you rewind the rewindable allocator, it invalidates and unregisters its child allocators, and invalidates all its child safety handles. For Native- collection types, the disposal safety checks throw an exception if the rewindable allocator has rewound.

    This example method UseRewindableAllocator shows how to use a rewindable allocator to create and allocate native containers:

    // Sample code to use rewindable allocator to allocate containers
    public unsafe void UseRewindableAllocator(out NativeArray<int> nativeArray, out NativeList<int> nativeList, out byte* bytePtr)
    {
        // Use rewindable allocator to allocate a native array, no need to dispose the array manually
        // CollectionHelper is required to create/allocate native array from a custom allocator.
        nativeArray = CollectionHelper.CreateNativeArray<int, RewindableAllocator>(100, ref RwdAllocator);
        nativeArray[0] = 0xFE;
    
        // Use rewindable allocator to allocate a native list, do not need to dispose the list manually
        nativeList = new NativeList<int>(RwdAllocator.Handle);
        for (int i = 0; i < 50; i++)
        {
            nativeList.Add(i);
        }
    
        // Use custom allocator to allocate a byte buffer.
        bytePtr = (byte*)AllocatorManager.Allocate(ref RwdAllocator, sizeof(byte), sizeof(byte), 10);
        bytePtr[0] = 0xAB;
    }
    

    Free all allocated memory of a rewindable allocator

    When you Rewind the rewindable allocator, it performs the following operations:

    • Invalidates and unregisters all the allocator handle's child allocators
    • Invalidates all its child safety handles

    The example method FreeRewindableAllocator shows how to Free all allocations from the rewindable allocator, with Rewind.

    // Free all allocations from the rewindable allocator
    public void FreeRewindableAllocator()
    {
        RwdAllocator.Rewind();
    }
    

    Dispose a rewindable allocator

    To dispose a rewindable allocator, you must do the following:

    • Dispose all the memory blocks of the rewindable allocator from Allocator.Persistant.
    • Unregister the allocator
    • Dispose the memory used to store the allocator

    The following example adds a method DisposeRewindableAllocatorthat disposes a rewindable allocator using Dispose:

    // Dispose the rewindable allocator
    void DisposeRewindableAllocator()
    {
        // Dispose all the memory blocks in the rewindable allocator
        RwdAllocator.Dispose();
        // Unregister the rewindable allocator and dispose it
        rwdAllocatorHelper.Dispose();
    }
    

    Full example of a rewindable allocator

    The following is a full example of how to use a rewindable allocator:

    using System;
    using NUnit.Framework;
    using Unity.Collections;
    
    // This is the example code used in
    // Packages/com.unity.collections/Documentation~/allocator/allocator-rewindable.md
    // Example user structure
    internal struct ExampleStruct
    {
        // Use AllocatorHelper to help creating a rewindable alloctor
        AllocatorHelper<RewindableAllocator> rwdAllocatorHelper;
    
        // Rewindable allocator property for accessibility
        public ref RewindableAllocator RwdAllocator => ref rwdAllocatorHelper.Allocator;
    
        // Create the rewindable allocator
        void CreateRewindableAllocator(AllocatorManager.AllocatorHandle backgroundAllocator, int initialBlockSize, bool enableBlockFree = false)
        {
            // Allocate the rewindable allocator from backgroundAllocator and register the allocator
            rwdAllocatorHelper = new AllocatorHelper<RewindableAllocator>(backgroundAllocator);
    
            // Allocate the first memory block with initialBlockSize in bytes, and indicate whether
            // to enable the rewindable allocator with individual block free through enableBlockFree
            RwdAllocator.Initialize(initialBlockSize, enableBlockFree);
        }
    
        // Constructor of user structure
        public ExampleStruct(int initialBlockSize)
        {
            this = default;
            CreateRewindableAllocator(Allocator.Persistent, initialBlockSize, false);
        }
    
        // Dispose the user structure
        public void Dispose()
        {
            DisposeRewindableAllocator();
        }
    
        // Sample code to use rewindable allocator to allocate containers
        public unsafe void UseRewindableAllocator(out NativeArray<int> nativeArray, out NativeList<int> nativeList, out byte* bytePtr)
        {
            // Use rewindable allocator to allocate a native array, no need to dispose the array manually
            // CollectionHelper is required to create/allocate native array from a custom allocator.
            nativeArray = CollectionHelper.CreateNativeArray<int, RewindableAllocator>(100, ref RwdAllocator);
            nativeArray[0] = 0xFE;
    
            // Use rewindable allocator to allocate a native list, do not need to dispose the list manually
            nativeList = new NativeList<int>(RwdAllocator.Handle);
            for (int i = 0; i < 50; i++)
            {
                nativeList.Add(i);
            }
    
            // Use custom allocator to allocate a byte buffer.
            bytePtr = (byte*)AllocatorManager.Allocate(ref RwdAllocator, sizeof(byte), sizeof(byte), 10);
            bytePtr[0] = 0xAB;
        }
    
        // Free all allocations from the rewindable allocator
        public void FreeRewindableAllocator()
        {
            RwdAllocator.Rewind();
        }
    
        // Dispose the rewindable allocator
        void DisposeRewindableAllocator()
        {
            // Dispose all the memory blocks in the rewindable allocator
            RwdAllocator.Dispose();
            // Unregister the rewindable allocator and dispose it
            rwdAllocatorHelper.Dispose();
        }
    }
    internal class ExampleStructSampleUsage
    {
        // Initial block size of the rewindable allocator.
        const int IntialBlockSize = 128 * 1024;
    
        [Test]
        public unsafe void UseRewindableAllocator_Works()
        {
            ExampleStruct exampleStruct = new ExampleStruct(IntialBlockSize);
    
            // Allocate native array and native list from rewindable allocator
            exampleStruct.UseRewindableAllocator(out NativeArray<int> nativeArray, out NativeList<int> nativeList, out byte* bytePtr);
    
            // Still able to access the native array, native list and byte buffer
            Assert.AreEqual(nativeArray[0], 0xFE);
            Assert.AreEqual(nativeList[10], 10);
            Assert.AreEqual(bytePtr[0], 0xAB);
    
    
            // Free all memories allocated from the rewindable allocator
            // No need to dispose the native array and native list
            exampleStruct.FreeRewindableAllocator();
    
    #if ENABLE_UNITY_COLLECTIONS_CHECKS
            // Object disposed exception throws because nativeArray is already disposed
            Assert.Throws<ObjectDisposedException>(() =>
            {
                nativeArray[0] = 0xEF;
            });
    
            // Object disposed exception throws because nativeList is already disposed
            Assert.Throws<ObjectDisposedException>(() =>
            {
                nativeList[10] = 0x10;
            });
    #endif
    
            // Dispose the user structure
            exampleStruct.Dispose();
        }
    }
    
    In This Article
    Back to top
    Copyright © 2024 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)