LLVM 数据结构简介

LLVM 数据结构简介

  • [LLVM 数据结构简介](#LLVM 数据结构简介)
    • ArrayRef
      • 特点
      • [与标准库 array 对比](#与标准库 array 对比)
    • SmallVector
      • [与标准库 vector 的对比](#与标准库 vector 的对比)

LLVM 数据结构简介

ArrayRef

llvm::ArrayRef 是一个轻量级的只读容器,主要用于引用一段连续的内存区域。它的设计目标是提供高效的数据访问,而不需要拥有底层数据的所有权。这使得 ArrayRef 特别适合在函数参数中按值传递,从而避免了不必要的内存拷贝。

特点

  • 只读:ArrayRef 不能修改其引用的数据,也不能添加新元素(另一个容器 MutableArrayRef 可以修改)。
  • 轻量级:它只存储一个指向数据的指针和数据的长度,而不存储实际的数据,所以拷贝时非常高效。
  • 按值传递:在传递 ArrayRef 时,实际上传递的是一个指针和其指向数据的长度,所以不需要再对其按引用传递。
  • 操作一致:它的大多数操作,与 STL array 保持一致。

与标准库 array 对比

  • 所有权:llvm::ArrayRef 不拥有其引用数据的所有权,只是对数据的引用;std::array 拥有数据的所有权,存储在栈上。
  • 大小:llvm::ArrayRef 容量是动态的,可以引用任意长度的数组。但由于数组长度是静态的,所以从程序角度看,ArrayRef 的具体引用类型,容量是确定的;std::array 大小在编译期间固定,和 C 数组一样。
  • 可变性:llvm::ArrayRef 是只读的,不能修改引用数据;std::array 允许修改其元素,提供完整的读写权限。
  • 初始化:llvm::ArrayRef 的初始化更灵活,可以从 C 数组、std::array,std::vector 或其他顺序容器初始化。std::array 只能从初始化列表或在定义时使用构造函数初始化。
  • 性能:llvm::ArrayRef 适合在高频传递参数时使用。std::array 默认按值拷贝,会带来开销,需要指定其引用类型作为参数类型。如果是 constexpr 修饰,编译器可以优化传参性能。
c 复制代码
template <typename T>class ArrayRef {
private:
    const T* data = nullptr;
    size_t length = 0;
}

SmallVector

SmallVector是llvm中自定义的一种通用数据结构,在llvm的各层次间都可以使用。SmallVector与std::Vector非常类似 ,支持迭代、push_back、pop_back,以及随机存取元素。

SmallVector对与元素较少的情况时性能是优与std::vector的。 这是因为SmallVector使用了一种比较通用的局部缓存设计模式,减少了malloc/free的巨大开销。

std::vector会调用malloc函数申请一块内存用于放置元素,std::array是对内置数组的一个封装,因此其存放元素的数组会与std::array放置在同一个位置。如果std::array是在栈上声明的,那么其存放元素的数组也位于栈上。

内存的位置不同导致了std::array与std::vector效率的不同。因为std::vector是通过malloc申请的堆内存,而std::array是栈内存。

堆内存的申请需要调用系统函数分配内存,这个开销是巨大的 。相比之下,栈内存几乎是零开销,因为值需要调整栈指针。当然std::array也并不总是在栈上的,取决与你分配它的方式。但是任然会比std::vector少分配一次堆内存。

smallvector的本质就是当元素个数少的时候像std::array一样将存储元素的内存放在类里面,当元素个数多的时候像std::vector一样分配堆内存。这样就兼具了两者的优点。 这种操作被称为局部缓存设计模式,在llvm很多地方都有体现

c 复制代码
template<typename T, unsigned N = CalculateSmallVectorDefaultInlinedElements<T>::value> 
class SmallVector;

llvm::SmallVector 是一个可变长数组,类似于 std::vector,同时它对较小长度的数组做了优化。它的内存管理方式采用局部缓存的设计思路,在对象内部预留一小块空间,用于存储数据。当数据量超出预留空间的大小时,才会将数据放在堆上。它本身保存一部分元素,这便使得在小数组中,避免进行堆分配的操作,提高了效率。

注意到,它带有一个含默认值的模板参数 N,它用来指定预留空间的大小,默认不指定时,编译器会自动选择一个合理的阈值(通常考虑依据是栈空间的占用情况)。

与标准库 vector 的对比

  • 性能优势:在数据量较小时,由于不会涉及到堆内存分配和管理的开销,性能会优于 std::vector。另外,它可以识别平凡可复制的特性,从而更细粒度地做内存管理
  • 退化时的性能损失:当发生从栈到堆的退化时,llvm::SmallVector 会带来数据拷贝的开销。所以需要仔细考虑阈值的设定。
  • 可能浪费空间:llvm::SmallVector 会在定义时预分配设定的 N 的空间,如果实际数据少于 N,那么会存在空间浪费的问题。std::vector 也存在类似问题。

SmallVector,它继承了SmallVectorImpl和SmallVectorStorage。SmallVector本身只有一系列构造函数(拷贝构造、赋值构造等),没有成员变量,具体的一些成员函数放在SmallVectorImpl中,存储元素的数组放在SmallVectorStorage中。 SmallVectorStorage类中直接声明了一个数组。

c 复制代码
/// Storage for the SmallVector elements.  This is specialized for the N=0 case
/// to avoid allocating unnecessary storage.
template <typename T, unsigned N>
struct SmallVectorStorage { 
  // alignas(T) 强制编译器为 InlineElts 数组分配内存时,其起始地址必须满足 T 类型的对齐要求。
  // 确保 InlineElts 这个原始字节数组的起始地址满足类型 T 的内存对齐要求,
  // 从而使得后续我们可以安全地在此数组的内存上直接构造 T 类型的对象,
  // 避免因内存未对齐而导致的未定义行为
  alignas(T) char InlineElts[N * sizeof(T)];
  //在 C++11 中,标准库提供了 std::aligned_storage 来专门处理这种情况
  //std::aligned_storage_t<sizeof(T), alignof(T)> InlineElts[N];
};

拥有小型缓冲区优化

这个数组会和对象放在同一块内存,当需要存放的元素较少时就可以放在这个数组中,避免了调用malloc产生巨大的开销。

相关推荐
John_ToDebug2 小时前
浏览器稳定性提升之路:线上崩溃率优化中的 Return 与 CHECK 之争
c++·chrome
dragoooon342 小时前
[优选算法专题二——NO.16最小覆盖子串]
c++·算法·leetcode·学习方法
Yeats_Liao2 小时前
Java 软件测试(三):Mockito打桩与静态方法模拟解析
java·开发语言
JAVA学习通2 小时前
RabbitMQ---面试题
java·开发语言
汉克老师2 小时前
第十四届蓝桥杯青少组C++选拔赛[2023.1.15]第二部分编程题(4 、移动石子)
c++·算法·蓝桥杯·蓝桥杯c++·c++蓝桥杯
艾菜籽2 小时前
UDP套接字的使用
java·开发语言·网络
云天徽上2 小时前
【数据可视化-111】93大阅兵后的军费开支情况———2024年全球军费开支分析:用Python和Pyecharts打造炫酷可视化大屏
开发语言·python·信息可视化·pyecharts
zhangfeng11332 小时前
错误于make.names(vnames, unique = TRUE): invalid multibyte string 9 使用 R 语言进行数据处理时
开发语言·r语言·生物信息
七夜zippoe2 小时前
缓存三大劫攻防战:穿透、击穿、雪崩的Java实战防御体系(三)
java·开发语言·缓存