假设一个简单的部分评估方案:
#include <vector>
/* may be known at runtime */
int someConstant();
/* can be partially evaluated */
double foo(std::vector<double> args) {
return args[someConstant()] * someConstant();
}
假设someConstant()
已知并且在运行时不会更改(例如由用户提供一次),并且可以由相应的int literal替换。如果foo
是热门路径的一部分,我预计会有显着的性能提升:
/* partially evaluated, someConstant() == 2 */
double foo(std::vector<double> args) {
return args[2] * 2;
}
我目前对该问题的看法是在运行时生成LLVM IR,因为我知道部分评估代码的结构(因此我不需要通用的部分求值程序)。
所以我想编写一个函数foo_ir
来生成与foo
完全相同但不调用someConstant()
的IR代码,因为它在运行时是已知的。
很简单,不是吗?然而,当我查看上面代码生成的IR时:
; Function Attrs: uwtable
define double @_Z3fooSt6vectorIdSaIdEE(%"class.std::vector"* %args) #0 {
%1 = call i32 @_Z12someConstantv()
%2 = sext i32 %1 to i64
%3 = call double* @_ZNSt6vectorIdSaIdEEixEm(%"class.std::vector"* %args, i64 %2)
%4 = load double* %3
%5 = call i32 @_Z12someConstantv()
%6 = sitofp i32 %5 to double
%7 = fmul double %4, %6
ret double %7
}
; Function Attrs: nounwind uwtable
define linkonce_odr double* @_ZNSt6vectorIdSaIdEEixEm(%"class.std::vector"* %this, i64 %__n) #1 align 2 {
%1 = alloca %"class.std::vector"*, align 8
%2 = alloca i64, align 8
store %"class.std::vector"* %this, %"class.std::vector"** %1, align 8
store i64 %__n, i64* %2, align 8
%3 = load %"class.std::vector"** %1
%4 = bitcast %"class.std::vector"* %3 to %"struct.std::_Vector_base"*
%5 = getelementptr inbounds %"struct.std::_Vector_base"* %4, i32 0, i32 0
%6 = getelementptr inbounds %"struct.std::_Vector_base<double, std::allocator<double> >::_Vector_impl"* %5, i32 0, i32 0
%7 = load double** %6, align 8
%8 = load i64* %2, align 8
%9 = getelementptr inbounds double* %7, i64 %8
ret double* %9
}
我看到,[]
包含在STL定义(函数@_ZNSt6vectorIdSaIdEEixEm
)中 - 足够公平。问题是:它可能是一些成员函数,甚至是直接数据访问,我根本无法假设数据布局在任何地方都是相同的,所以在开发时,我不知道具体的std::vector
主机的布局。
有没有办法使用C ++元编程在编译时获取所需的信息?即是否有某种方法可以让llvm为std::vector
的{{1}}方法提供IR?
作为奖励:我宁愿不用clang强制编译库,相反,LLVM应该是运行时依赖,所以只需在编译时调用clang(即使我不知道如何执行此操作)是第二好的解决方案。
答案 0 :(得分:3)
回答我自己的问题:
虽然我仍然没有针对一般情况的解决方案(例如std::map
),但std::vector
存在一个简单的解决方案:
根据the C++ standard,以下适用于成员函数data()
返回指向内部使用的内存数组的直接指针 矢量来存储其拥有的元素。
因为矢量中的元素保证存储在 连续存储位置的顺序与表示的顺序相同 向量,检索到的指针可以被偏移以访问任何元素 数组。
事实上,std::vector
的对象级布局由标准修复。