美文网首页
15 UE5 SetAllocator介绍

15 UE5 SetAllocator介绍

作者: 游戏开发程序员 | 来源:发表于2024-03-06 09:31 被阅读0次

SetAllocator

  • 用于SET MAP 容器的默认内存分配器
  • class FDefaultSetAllocator : public TSetAllocator<>
  • Allocator = FDefaultSetAllocator> class TSet
#if !defined(DEFAULT_NUMBER_OF_ELEMENTS_PER_HASH_BUCKET)
#   define DEFAULT_NUMBER_OF_ELEMENTS_PER_HASH_BUCKET   2
#endif
#define DEFAULT_BASE_NUMBER_OF_HASH_BUCKETS             8
#define DEFAULT_MIN_NUMBER_OF_HASHED_ELEMENTS           4

TSetAllocator的模板参数

template<
    typename InSparseArrayAllocator               = TSparseArrayAllocator<>,
    typename InHashAllocator                      = TInlineAllocator<1,FDefaultAllocator>,
    uint32   AverageNumberOfElementsPerHashBucket = DEFAULT_NUMBER_OF_ELEMENTS_PER_HASH_BUCKET,
    uint32   BaseNumberOfHashBuckets              = DEFAULT_BASE_NUMBER_OF_HASH_BUCKETS,
    uint32   MinNumberOfHashedElements            = DEFAULT_MIN_NUMBER_OF_HASHED_ELEMENTS
    >
class TSetAllocator
{
......
    typedef InSparseArrayAllocator SparseArrayAllocator;
    typedef InHashAllocator        HashAllocator;
}

两个内存分配器的存储

  • 数据存储区 InSparseArrayAllocator 稀疏数组
  • 内部实际存储了TSetElement
  • Hash存储区 InHashAllocator(TInlineAllocator)可扩展的
  • 内部为 数据存储区(SparseArray)的下标

TSparseArrayAllocator

  • 数据存储区,里面存放多个数据。
  • 是用来给set map使用的数据存储器
/** Encapsulates the allocators used by a sparse array in a single type. */
template<typename InElementAllocator = FDefaultAllocator,typename InBitArrayAllocator = FDefaultBitArrayAllocator>
class TSparseArrayAllocator
{
public:

    typedef InElementAllocator ElementAllocator;
    typedef InBitArrayAllocator BitArrayAllocator;
};

union类型 TSparseArrayElementOrFreeListLink

  • 在安放数据的时候,ElementData就是实际的元素内容
  • 在没有安放数据的时候,PrevFreeIndex 和 NextFreeIndex 就是前/后指针。
template<typename ElementType>
union TSparseArrayElementOrFreeListLink
{
    // 有数据,这里是内容
    ElementType ElementData;

    // 无数据,这里是前后指针
    struct
    {
        int32 PrevFreeIndex;
        int32 NextFreeIndex;
    };
};

TSet添加元素

  • 在实际存储Elements稀疏数组中构造元素
  • 调用TryReplaceExisting 有旧值替换
  • 判断是否需要RehashOrLink
  • 返回FSetElementId(元素在Elements集合中的索引地址)
    template <typename ArgsType = ElementType>
    FSetElementId Emplace(ArgsType&& Args, bool* bIsAlreadyInSetPtr = nullptr)
    {
        // Create a new element.
        FSparseArrayAllocationInfo ElementAllocation = Elements.AddUninitialized();
        SetElementType& Element = *new (ElementAllocation) SetElementType(Forward<ArgsType>(Args));

        SizeType NewHashIndex = ElementAllocation.Index;

        uint32 KeyHash = KeyFuncs::GetKeyHash(KeyFuncs::GetSetKey(Element.Value));
        if (!TryReplaceExisting(KeyHash, Element, NewHashIndex, bIsAlreadyInSetPtr))
        {
            RehashOrLink(KeyHash, Element, NewHashIndex);
        }
        return FSetElementId(NewHashIndex);
    }

TryReplaceExisting分析

  • 1 通过插入的值获取hashkey
  • 2 判断是否存在,已存在用新值更新旧值
  • 3返回 是否已存在set中 bIsAlreadyInSet
    bool TryReplaceExisting(uint32 KeyHash, SetElementType& Element, 
SizeType& InOutElementIndex, bool* bIsAlreadyInSetPtr)
    {
        bool bIsAlreadyInSet = false;
        // 如果不允许重复的KEY
        if (!KeyFuncs::bAllowDuplicateKeys)
        {
            // 添加第一个元素时不用麻烦了
            if (Elements.Num() != 1)
            {
                SizeType ExistingIndex = FindIndexByHash(KeyHash, KeyFuncs::GetSetKey(Element.Value));

                bIsAlreadyInSet = ExistingIndex != INDEX_NONE;
                if (bIsAlreadyInSet)
                {
                    //  用新的元素替换掉旧的
                    MoveByRelocate(Elements[ExistingIndex].Value, Element.Value);

                    Elements.RemoveAtUninitialized(InOutElementIndex);

                    // 返回索引的更新
                    InOutElementIndex = ExistingIndex;
                }
            }
        }
        if (bIsAlreadyInSetPtr)
        {
            *bIsAlreadyInSetPtr = bIsAlreadyInSet;
        }
        return bIsAlreadyInSet;
    }

TSet查找元素

  • 可以看到就是返回 存储数组中的下标
    FORCEINLINE ElementType* Find(KeyInitType Key)
    {
        SizeType ElementIndex = FindIndexByHash(KeyFuncs::GetKeyHash(Key), Key);
        if (ElementIndex != INDEX_NONE)
        {
            return &Elements[ElementIndex].Value;
        }
        else
        {
            return nullptr;
        }
    }

FindIndexByHash函数分析

  • TSetElement:TSetElementBase
  • TSetElementBase:成员有:
  • 元素的值:Value
  • 同一哈希桶中下一个元素的id : HashNextId
  • 某元素当前链接到的哈希桶:HashIndex
    HashIndex
    template <typename ComparableKey>
    SizeType FindIndexByHash(uint32 KeyHash, const ComparableKey& Key) const
    {
        if (Elements.Num() == 0)
        {
            return INDEX_NONE;
        }

        FSetElementId* HashPtr      = Hash.GetAllocation();
        SizeType       ElementIndex = HashPtr[KeyHash & (HashSize - 1)].Index;
        for (;;)
        {
            if (ElementIndex == INDEX_NONE)
            {
                return INDEX_NONE;
            }

            if (KeyFuncs::Matches(KeyFuncs::GetSetKey(Elements[ElementIndex].Value), Key))
            {
                // 第一个匹配就返回,哪怕是有多个键匹配
                return ElementIndex;
            }

            // 不匹配 就继续在HashNextId 寻找
            ElementIndex = Elements[ElementIndex].HashNextId.Index;
        }
    }
  • 行: 函数值对应的桶
  • 列: 链表结构的HASH相等的元素串在一起


    image.png

TMap的区别

  • 继承TSortableMapBase : TMapBase
  • 内存分配器使用SetAllocator
  • Emplace传递给Pairs.Emplace实现 大部分功能都转给Pairs
class TMap : public TSortableMapBase<InKeyType, InValueType, SetAllocator, KeyFuncs>
class TSortableMapBase : public TMapBase<KeyType, ValueType, SetAllocator, KeyFuncs>

    ValueType& Emplace(InitKeyType&& InKey, InitValueType&& InValue)
    {
        const FSetElementId PairId = Pairs.Emplace(
TPairInitializer<InitKeyType&&, InitValueType&&>(Forward<InitKeyType>(InKey), 
Forward<InitValueType>(InValue)));

        return Pairs[PairId].Value;
    }

    FORCEINLINE ValueType* Find(KeyConstPointerType Key)
    {
        if (auto* Pair = Pairs.Find(Key))
        {
            return &Pair->Value;
        }

        return nullptr;
    }

ElementSetType Pairs

  • Pairs就是Set<TPair<KeyType, ValueType>>
typedef TPair<KeyType, ValueType> ElementType;
typedef TSet<ElementType, KeyFuncs, SetAllocator> ElementSetType;
ElementSetType Pairs

相关文章

  • 程序打包

    关于UE5打包问题[https://www.bilibili.com/read/cv11679358] UE5 P...

  • 目录、资产命名规范

    【UE5】目录、资产命名规范[https://zhuanlan.zhihu.com/p/484119115]

  • 地理坐标转换

    关联GIS:条条道路通UE5城[https://zhuanlan.zhihu.com/p/528244402] 关...

  • 通讯

    开源篇-WebSocket搭建UE5通信桥梁[https://zhuanlan.zhihu.com/p/54621...

  • 调试

    UE4/UE5的崩溃,卡死等问题处理[https://zhuanlan.zhihu.com/p/565680732]

  • 源代码

    从零开始:编译UE5 source code[https://www.jianshu.com/p/4a6b8603...

  • UE 命名规范

    资产命名表格链接:UE5项目命名规则[https://link.zhihu.com/?target=https%3...

  • UE5蓝图-动态创建和查找模型

    UE5蓝图-动态创建和查找模型,并控制其显隐性 蓝图 BP_RedEarth 蓝图 BP_CreateRedEar...

  • 天登野外课

    自我介绍 9:00-9:15 记忆介绍9:15-9:25 抛物介绍 线路介绍 9:25-9:35距离,路况紧急...

  • 【UE5】Nanite解析

    Epic外放的两大特性Nanite跟Lumen,构成了UE版本升级的基石,关于这两大技术,已经有了众多的分享,不过...

网友评论

      本文标题:15 UE5 SetAllocator介绍

      本文链接:https://www.haomeiwen.com/subject/cyynadtx.html