Optimize dynamic memory allocations for managed runtime interop collections with a new Arena Allocation

This commit is contained in:
Wojtek Figat
2025-05-25 17:39:20 +02:00
parent 9aaba955d0
commit 8c62f1120f
13 changed files with 185 additions and 55 deletions

View File

@@ -20,6 +20,7 @@ API_CLASS(InBuild) class Array
public:
using ItemType = T;
using AllocationData = typename AllocationType::template Data<T>;
using AllocationTag = typename AllocationType::Tag;
private:
int32 _count;
@@ -36,6 +37,17 @@ public:
{
}
/// <summary>
/// Initializes an empty <see cref="Array"/> without reserving any space.
/// </summary>
/// <param name="tag">The custom allocation tag.</param>
Array(AllocationTag tag)
: _count(0)
, _capacity(0)
, _allocation(tag)
{
}
/// <summary>
/// Initializes <see cref="Array"/> by reserving space.
/// </summary>

View File

@@ -36,6 +36,17 @@ namespace AllocationUtils
capacity++;
return capacity;
}
inline int32 CalculateCapacityGrow(int32 capacity, int32 minCapacity)
{
if (capacity < minCapacity)
capacity = minCapacity;
if (capacity < 8)
capacity = 8;
else
capacity = RoundUpToPowerOf2(capacity);
return capacity;
}
}
/// <summary>
@@ -46,6 +57,7 @@ class FixedAllocation
{
public:
enum { HasSwap = false };
typedef void* Tag;
template<typename T>
class alignas(sizeof(void*)) Data
@@ -58,6 +70,10 @@ public:
{
}
FORCE_INLINE Data(Tag tag)
{
}
FORCE_INLINE ~Data()
{
}
@@ -106,6 +122,7 @@ class HeapAllocation
{
public:
enum { HasSwap = true };
typedef void* Tag;
template<typename T>
class Data
@@ -118,6 +135,10 @@ public:
{
}
FORCE_INLINE Data(Tag tag)
{
}
FORCE_INLINE ~Data()
{
Allocator::Free(_data);
@@ -135,13 +156,7 @@ public:
FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, const int32 minCapacity) const
{
if (capacity < minCapacity)
capacity = minCapacity;
if (capacity < 8)
capacity = 8;
else
capacity = AllocationUtils::RoundUpToPowerOf2(capacity);
return capacity;
return AllocationUtils::CalculateCapacityGrow(capacity, minCapacity);
}
FORCE_INLINE void Allocate(const int32 capacity)
@@ -184,6 +199,7 @@ class InlinedAllocation
{
public:
enum { HasSwap = false };
typedef void* Tag;
template<typename T>
class alignas(sizeof(void*)) Data
@@ -200,6 +216,10 @@ public:
{
}
FORCE_INLINE Data(Tag tag)
{
}
FORCE_INLINE ~Data()
{
}

View File

@@ -1,5 +1,7 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#include "Allocation.h"
/// <summary>
@@ -63,3 +65,80 @@ public:
collection.Clear();
}
};
/// <summary>
/// The memory allocation policy that uses a part of shared page allocator. Allocations are performed in stack-manner, and free is no-op.
/// </summary>
class ArenaAllocation
{
public:
enum { HasSwap = true };
typedef ArenaAllocator* Tag;
template<typename T>
class Data
{
private:
T* _data = nullptr;
ArenaAllocator* _arena = nullptr;
public:
FORCE_INLINE Data()
{
}
FORCE_INLINE Data(Tag tag)
{
_arena = tag;
}
FORCE_INLINE ~Data()
{
}
FORCE_INLINE T* Get()
{
return _data;
}
FORCE_INLINE const T* Get() const
{
return _data;
}
FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, const int32 minCapacity) const
{
return AllocationUtils::CalculateCapacityGrow(capacity, minCapacity);
}
FORCE_INLINE void Allocate(const int32 capacity)
{
ASSERT_LOW_LAYER(!_data && _arena);
_data = (T*)_arena->Allocate(capacity * sizeof(T), alignof(T));
}
FORCE_INLINE void Relocate(const int32 capacity, int32 oldCount, int32 newCount)
{
ASSERT_LOW_LAYER(_arena);
T* newData = capacity != 0 ? (T*)_arena->Allocate(capacity * sizeof(T), alignof(T)) : nullptr;
if (oldCount)
{
if (newCount > 0)
Memory::MoveItems(newData, _data, newCount);
Memory::DestructItems(_data, oldCount);
}
_data = newData;
}
FORCE_INLINE void Free()
{
_data = nullptr;
}
FORCE_INLINE void Swap(Data& other)
{
::Swap(_data, other._data);
::Swap(_arena, other._arena);
}
};
};

View File

@@ -11,6 +11,7 @@ class SimpleHeapAllocation
{
public:
enum { HasSwap = true };
typedef void* Tag;
template<typename T>
class Data
@@ -23,6 +24,10 @@ public:
{
}
FORCE_INLINE Data(Tag tag)
{
}
FORCE_INLINE ~Data()
{
if (_data)

View File

@@ -815,7 +815,7 @@ namespace
{
MMethod* FindMethod(MClass* mclass, const MMethod* referenceMethod)
{
const Array<MMethod*>& methods = mclass->GetMethods();
const auto& methods = mclass->GetMethods();
for (int32 i = 0; i < methods.Count(); i++)
{
MMethod* method = methods[i];
@@ -1095,7 +1095,7 @@ void ManagedBinaryModule::InitType(MClass* mclass)
// Initialize scripting interfaces implemented in C#
int32 interfacesCount = 0;
MClass* klass = mclass;
const Array<MClass*>& interfaceClasses = klass->GetInterfaces();
const auto& interfaceClasses = klass->GetInterfaces();
for (const MClass* interfaceClass : interfaceClasses)
{
const ScriptingTypeHandle interfaceType = FindType(interfaceClass);

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Memory/ArenaAllocation.h"
#include "MTypes.h"
/// <summary>
@@ -25,12 +26,12 @@ private:
#endif
MAssembly* _assembly;
mutable Array<MMethod*> _methods;
mutable Array<MField*> _fields;
mutable Array<MProperty*> _properties;
mutable Array<MObject*> _attributes;
mutable Array<MEvent*> _events;
mutable Array<MClass*> _interfaces;
mutable Array<MMethod*, ArenaAllocation> _methods;
mutable Array<MField*, ArenaAllocation> _fields;
mutable Array<MProperty*, ArenaAllocation> _properties;
mutable Array<MObject*, ArenaAllocation> _attributes;
mutable Array<MEvent*, ArenaAllocation> _events;
mutable Array<MClass*, ArenaAllocation> _interfaces;
MVisibility _visibility;
@@ -248,7 +249,7 @@ public:
/// </summary>
/// <remarks>Be aware this will not include the methods of any base classes.</remarks>
/// <returns>The list of methods.</returns>
const Array<MMethod*>& GetMethods() const;
const Array<MMethod*, ArenaAllocation>& GetMethods() const;
/// <summary>
/// Returns an object referencing a field with the specified name.
@@ -263,7 +264,7 @@ public:
/// </summary>
/// <remarks>Be aware this will not include the fields of any base classes.</remarks>
/// <returns>The list of fields.</returns>
const Array<MField*>& GetFields() const;
const Array<MField*, ArenaAllocation>& GetFields() const;
/// <summary>
/// Returns an object referencing a event with the specified name.
@@ -276,7 +277,7 @@ public:
/// Returns all events belonging to this class.
/// </summary>
/// <returns>The list of events.</returns>
const Array<MEvent*>& GetEvents() const;
const Array<MEvent*, ArenaAllocation>& GetEvents() const;
/// <summary>
/// Returns an object referencing a property with the specified name.
@@ -291,14 +292,14 @@ public:
/// </summary>
/// <remarks>Be aware this will not include the properties of any base classes.</remarks>
/// <returns>The list of properties.</returns>
const Array<MProperty*>& GetProperties() const;
const Array<MProperty*, ArenaAllocation>& GetProperties() const;
/// <summary>
/// Returns all interfaces implemented by this class (excluding interfaces from base classes).
/// </summary>
/// <remarks>Be aware this will not include the interfaces of any base classes.</remarks>
/// <returns>The list of interfaces.</returns>
const Array<MClass*>& GetInterfaces() const;
const Array<MClass*, ArenaAllocation>& GetInterfaces() const;
public:
/// <summary>
@@ -332,5 +333,5 @@ public:
/// Returns an instance of all attributes connected with given class. Returns null if the class doesn't have any attributes.
/// </summary>
/// <returns>The array of attribute objects.</returns>
const Array<MObject*>& GetAttributes() const;
const Array<MObject*, ArenaAllocation>& GetAttributes() const;
};

View File

@@ -29,7 +29,7 @@ protected:
mutable int32 _hasAddMonoMethod : 1;
mutable int32 _hasRemoveMonoMethod : 1;
mutable Array<MObject*> _attributes;
mutable Array<MObject*, ArenaAllocation> _attributes;
public:
#if USE_MONO
@@ -121,5 +121,5 @@ public:
/// Returns an instance of all attributes connected with given event. Returns null if the event doesn't have any attributes.
/// </summary>
/// <returns>The array of attribute objects.</returns>
const Array<MObject*>& GetAttributes() const;
const Array<MObject*, ArenaAllocation>& GetAttributes() const;
};

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Memory/ArenaAllocation.h"
#include "MTypes.h"
/// <summary>
@@ -32,7 +33,7 @@ protected:
mutable int32 _hasCachedAttributes : 1;
int32 _isStatic : 1;
mutable Array<MObject*> _attributes;
mutable Array<MObject*, ArenaAllocation> _attributes;
public:
#if USE_MONO
@@ -157,5 +158,5 @@ public:
/// Returns an instance of all attributes connected with given field. Returns null if the field doesn't have any attributes.
/// </summary>
/// <returns>The array of attribute objects.</returns>
const Array<MObject*>& GetAttributes() const;
const Array<MObject*, ArenaAllocation>& GetAttributes() const;
};

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Memory/ArenaAllocation.h"
#if COMPILE_WITH_PROFILER
#include "Engine/Profiler/ProfilerSrcLoc.h"
#endif
@@ -42,7 +43,7 @@ protected:
#endif
int32 _isStatic : 1;
mutable Array<MObject*> _attributes;
mutable Array<MObject*, ArenaAllocation> _attributes;
public:
#if USE_MONO
@@ -197,5 +198,5 @@ public:
/// Returns an instance of all attributes connected with given method. Returns null if the method doesn't have any attributes.
/// </summary>
/// <returns>The array of attribute objects.</returns>
const Array<MObject*>& GetAttributes() const;
const Array<MObject*, ArenaAllocation>& GetAttributes() const;
};

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Memory/ArenaAllocation.h"
#include "MTypes.h"
/// <summary>
@@ -31,7 +32,7 @@ protected:
mutable int32 _hasSetMethod : 1;
mutable int32 _hasGetMethod : 1;
mutable Array<MObject*> _attributes;
mutable Array<MObject*, ArenaAllocation> _attributes;
public:
#if USE_MONO
@@ -135,5 +136,5 @@ public:
/// Returns an instance of all attributes connected with given property. Returns null if the property doesn't have any attributes.
/// </summary>
/// <returns>The array of attribute objects.</returns>
const Array<MObject*>& GetAttributes() const;
const Array<MObject*, ArenaAllocation>& GetAttributes() const;
};

View File

@@ -212,7 +212,7 @@ MClass* GetClass(MType* typeHandle);
MClass* GetOrCreateClass(MType* typeHandle);
MType* GetObjectType(MObject* obj);
void* GetCustomAttribute(const Array<MObject*>& attributes, const MClass* attributeClass)
void* GetCustomAttribute(const Array<MObject*, ArenaAllocation>& attributes, const MClass* attributeClass)
{
for (MObject* attr : attributes)
{
@@ -223,7 +223,7 @@ void* GetCustomAttribute(const Array<MObject*>& attributes, const MClass* attrib
return nullptr;
}
void GetCustomAttributes(Array<MObject*>& result, void* handle, void* getAttributesFunc)
void GetCustomAttributes(Array<MObject*, ArenaAllocation>& result, void* handle, void* getAttributesFunc)
{
MObject** attributes;
int numAttributes;
@@ -922,6 +922,12 @@ MClass::MClass(MAssembly* parentAssembly, void* handle, const char* name, const
, _namespace(parentAssembly->AllocString(namespace_))
, _fullname(parentAssembly->AllocString(fullname))
, _assembly(parentAssembly)
, _methods(&parentAssembly->Memory)
, _fields(&parentAssembly->Memory)
, _properties(&parentAssembly->Memory)
, _attributes(&parentAssembly->Memory)
, _events(&parentAssembly->Memory)
, _interfaces(&parentAssembly->Memory)
, _hasCachedProperties(false)
, _hasCachedFields(false)
, _hasCachedMethods(false)
@@ -1050,7 +1056,7 @@ MMethod* MClass::GetMethod(const char* name, int32 numParams) const
return nullptr;
}
const Array<MMethod*>& MClass::GetMethods() const
const Array<MMethod*, ArenaAllocation>& MClass::GetMethods() const
{
if (_hasCachedMethods)
return _methods;
@@ -1089,7 +1095,7 @@ MField* MClass::GetField(const char* name) const
return nullptr;
}
const Array<MField*>& MClass::GetFields() const
const Array<MField*, ArenaAllocation>& MClass::GetFields() const
{
if (_hasCachedFields)
return _fields;
@@ -1116,7 +1122,7 @@ const Array<MField*>& MClass::GetFields() const
return _fields;
}
const Array<MEvent*>& MClass::GetEvents() const
const Array<MEvent*, ArenaAllocation>& MClass::GetEvents() const
{
if (_hasCachedEvents)
return _events;
@@ -1139,7 +1145,7 @@ MProperty* MClass::GetProperty(const char* name) const
return nullptr;
}
const Array<MProperty*>& MClass::GetProperties() const
const Array<MProperty*, ArenaAllocation>& MClass::GetProperties() const
{
if (_hasCachedProperties)
return _properties;
@@ -1166,7 +1172,7 @@ const Array<MProperty*>& MClass::GetProperties() const
return _properties;
}
const Array<MClass*>& MClass::GetInterfaces() const
const Array<MClass*, ArenaAllocation>& MClass::GetInterfaces() const
{
if (_hasCachedInterfaces)
return _interfaces;
@@ -1206,7 +1212,7 @@ MObject* MClass::GetAttribute(const MClass* klass) const
return (MObject*)GetCustomAttribute(GetAttributes(), klass);
}
const Array<MObject*>& MClass::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MClass::GetAttributes() const
{
if (_hasCachedAttributes)
return _attributes;
@@ -1239,6 +1245,7 @@ MEvent::MEvent(MClass* parentClass, void* handle, const char* name)
, _hasCachedAttributes(false)
, _hasAddMonoMethod(true)
, _hasRemoveMonoMethod(true)
, _attributes(&parentClass->GetAssembly()->Memory)
{
}
@@ -1267,7 +1274,7 @@ MObject* MEvent::GetAttribute(const MClass* klass) const
return (MObject*)GetCustomAttribute(GetAttributes(), klass);
}
const Array<MObject*>& MEvent::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MEvent::GetAttributes() const
{
if (_hasCachedAttributes)
return _attributes;
@@ -1313,6 +1320,7 @@ MField::MField(MClass* parentClass, void* handle, const char* name, void* type,
, _parentClass(parentClass)
, _name(parentClass->GetAssembly()->AllocString(name))
, _hasCachedAttributes(false)
, _attributes(&parentClass->GetAssembly()->Memory)
{
switch (attributes & MFieldAttributes::FieldAccessMask)
{
@@ -1389,7 +1397,7 @@ MObject* MField::GetAttribute(const MClass* klass) const
return (MObject*)GetCustomAttribute(GetAttributes(), klass);
}
const Array<MObject*>& MField::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MField::GetAttributes() const
{
if (_hasCachedAttributes)
return _attributes;
@@ -1410,6 +1418,7 @@ MMethod::MMethod(MClass* parentClass, StringAnsiView name, void* handle, int32 p
, _name(name)
, _hasCachedAttributes(false)
, _hasCachedSignature(false)
, _attributes(&parentClass->GetAssembly()->Memory)
{
switch (attributes & MMethodAttributes::MemberAccessMask)
{
@@ -1555,7 +1564,7 @@ MObject* MMethod::GetAttribute(const MClass* klass) const
return (MObject*)GetCustomAttribute(GetAttributes(), klass);
}
const Array<MObject*>& MMethod::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MMethod::GetAttributes() const
{
if (_hasCachedAttributes)
return _attributes;
@@ -1584,6 +1593,7 @@ MProperty::MProperty(MClass* parentClass, const char* name, void* handle, void*
, _name(parentClass->GetAssembly()->AllocString(name))
, _handle(handle)
, _hasCachedAttributes(false)
, _attributes(&parentClass->GetAssembly()->Memory)
{
_hasGetMethod = getterHandle != nullptr;
if (_hasGetMethod)
@@ -1644,7 +1654,7 @@ MObject* MProperty::GetAttribute(const MClass* klass) const
return (MObject*)GetCustomAttribute(GetAttributes(), klass);
}
const Array<MObject*>& MProperty::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MProperty::GetAttributes() const
{
if (_hasCachedAttributes)
return _attributes;

View File

@@ -1539,7 +1539,7 @@ MObject* MClass::GetAttribute(const MClass* klass) const
return attrInfo ? mono_custom_attrs_get_attr(attrInfo, klass->GetNative()) : nullptr;
}
const Array<MObject*>& MClass::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MClass::GetAttributes() const
{
if (_hasCachedAttributes)
return _attributes;
@@ -1662,7 +1662,7 @@ MObject* MEvent::GetAttribute(const MClass* klass) const
return foundAttr;
}
const Array<MObject*>& MEvent::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MEvent::GetAttributes() const
{
if (_hasCachedAttributes)
return _attributes;
@@ -1815,7 +1815,7 @@ MObject* MField::GetAttribute(const MClass* klass) const
return foundAttr;
}
const Array<MObject*>& MField::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MField::GetAttributes() const
{
if (_hasCachedAttributes)
return _attributes;
@@ -1988,7 +1988,7 @@ MObject* MMethod::GetAttribute(const MClass* klass) const
return foundAttr;
}
const Array<MObject*>& MMethod::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MMethod::GetAttributes() const
{
if (_hasCachedAttributes)
return _attributes;
@@ -2118,7 +2118,7 @@ MObject* MProperty::GetAttribute(const MClass* klass) const
return foundAttr;
}
const Array<MObject*>& MProperty::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MProperty::GetAttributes() const
{
if (_hasCachedAttributes)
return _attributes;

View File

@@ -358,7 +358,7 @@ MMethod* MClass::GetMethod(const char* name, int32 numParams) const
return nullptr;
}
const Array<MMethod*>& MClass::GetMethods() const
const Array<MMethod*, ArenaAllocation>& MClass::GetMethods() const
{
_hasCachedMethods = true;
return _methods;
@@ -369,13 +369,13 @@ MField* MClass::GetField(const char* name) const
return nullptr;
}
const Array<MField*>& MClass::GetFields() const
const Array<MField*, ArenaAllocation>& MClass::GetFields() const
{
_hasCachedFields = true;
return _fields;
}
const Array<MEvent*>& MClass::GetEvents() const
const Array<MEvent*, ArenaAllocation>& MClass::GetEvents() const
{
_hasCachedEvents = true;
return _events;
@@ -386,7 +386,7 @@ MProperty* MClass::GetProperty(const char* name) const
return nullptr;
}
const Array<MProperty*>& MClass::GetProperties() const
const Array<MProperty*, ArenaAllocation>& MClass::GetProperties() const
{
_hasCachedProperties = true;
return _properties;
@@ -407,7 +407,7 @@ MObject* MClass::GetAttribute(const MClass* klass) const
return nullptr;
}
const Array<MObject*>& MClass::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MClass::GetAttributes() const
{
_hasCachedAttributes = true;
return _attributes;
@@ -449,7 +449,7 @@ MObject* MEvent::GetAttribute(const MClass* klass) const
return nullptr;
}
const Array<MObject*>& MEvent::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MEvent::GetAttributes() const
{
return _attributes;
}
@@ -501,7 +501,7 @@ MObject* MField::GetAttribute(const MClass* klass) const
return nullptr;
}
const Array<MObject*>& MField::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MField::GetAttributes() const
{
return _attributes;
}
@@ -556,7 +556,7 @@ MObject* MMethod::GetAttribute(const MClass* klass) const
return nullptr;
}
const Array<MObject*>& MMethod::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MMethod::GetAttributes() const
{
return _attributes;
}
@@ -603,7 +603,7 @@ MObject* MProperty::GetAttribute(const MClass* klass) const
return nullptr;
}
const Array<MObject*>& MProperty::GetAttributes() const
const Array<MObject*, ArenaAllocation>& MProperty::GetAttributes() const
{
return _attributes;
}