C++ 排列数组元素的最快方法
我想创建一个函数模板来排列数组的元素(例如C++ 排列数组元素的最快方法,c++,performance,optimization,permutation,C++,Performance,Optimization,Permutation,我想创建一个函数模板来排列数组的元素(例如ints或uint8\ts,或者其他),其中模板参数确定应用于数组的排列。这里我主要关心的是性能,所以函数需要尽可能快。下面是我迄今为止的尝试,以及一些性能测试代码 #包括 #包括 #包括 #包括 模板 结构向量{ std::数组m_数据{}; std::size\u t m\u size{0}; constexpr Vector()=默认值; constexpr向量(std::初始值设定项\u列表){ 用于(自动t:列表){ 推回(t); } } co
int
s或uint8\t
s,或者其他),其中模板参数确定应用于数组的排列。这里我主要关心的是性能,所以函数需要尽可能快。下面是我迄今为止的尝试,以及一些性能测试代码
#包括
#包括
#包括
#包括
模板
结构向量{
std::数组m_数据{};
std::size\u t m\u size{0};
constexpr Vector()=默认值;
constexpr向量(std::初始值设定项\u列表){
用于(自动t:列表){
推回(t);
}
}
constexpr void pop_back(){
m_尺寸--;
}
constexpr void push_back(T){
m_数据[m_大小]=t;
m_size++;
}
constexpr std::size\u t size()常量{
返回m_大小;
}
constexpr T&operator[](int n){
如果(n<0 | | n>=m|u大小){
抛出std::超出范围(“向量索引超出范围”);
}
返回m_数据[n];
}
常量表达式常量T和运算符[](int n)常量{
如果(n<0 | | n>=m|u大小){
抛出std::超出范围(“向量索引超出范围”);
}
返回m_数据[n];
}
};
结构Foo{
数组arr{5,2,4,7,1,3,0,6};
模板
void permute1(){
对于(int j=0;j您的循环是运行时的,因此您松开了constepr
,使用等效于constepr的循环似乎甚至可以进行更好的优化,即使是手动编写的循环:
template<Vector<Vector<int>> cycles>
void permute4(){
auto loop2 = [&]<std::size_t ...Js>(std::index_sequence<Js...>, auto I)
{
constexpr Vector<int> cycle = cycles[I];
int temp = arr[cycle[0]];
((arr[cycle[Js]] = arr[cycle[Js+1]]), ...);
arr[cycle[sizeof...(Js)]] = temp;
};
[&]<std::size_t ...Is>(std::index_sequence<Is...>)
{
(loop2(std::make_index_sequence<cycles[Is].size() - 1>(),
std::integral_constant<std::size_t, Is>{}),
...);
}(std::make_index_sequence<cycles.size()>());
}
模板
void permute4(){
自动循环2=[&](标准::索引_序列,自动I)
{
constexpr向量循环=循环[I];
int temp=arr[周期[0]];
((arr[cycle[Js]]=arr[cycle[Js+1]]),…);
arr[循环[大小…(Js)]]=温度;
};
[&](标准::索引_序列)
{
(loop2(std::make_index_sequence(),
std::积分_常数{}),
...);
}(std::make_index_sequence());
}
(以确保行为相同)
FWIW,标准库已经有了一个工具:@NathanOliver,但我没有计算下一个排列。我正在对数组应用固定排列,例如循环4个元素。您是否尝试过强制循环一路展开?Clang表示它#pragma Clang loop unroll(完整)
。更好的方法是:用模板编写循环,而不是为s编写。@HTNW我刚刚测试了#pragma-clang-loop-unroll(full)
,结果没有什么区别。除非绝对必要,否则我不想使用模板循环,因为它们非常难看。@Ben Odd,将其中一个放在permute2
的外循环上给了我灵感(在Godbolt上,叮当作响的行李箱)5倍的加速。但它还不够聪明,不知道如何处理最里面的循环…这真是令人印象深刻,不需要保留我的答案。
function | g++ | clang++
----------------------------------
permute1 | 616979 μs | 28000 μs
permute2 | 1512795 μs | 2433898 μs
permute3 | 601762 μs | 23977 μs
function | g++ | clang++
----------------------------------
permute1 | 607681 μs | 32471 μs
permute2 | 997331 μs | 2431138 μs
permute3 | 624083 μs | 24020 μs
function | g++ | clang++
----------------------------------
permute1 | 6545779 μs | 43103 μs
permute2 | 6799692 μs | 919098 μs
permute3 | 603414 μs | 24288 μs
function | g++ | clang++
----------------------------------
permute1 | 5478490 μs | 43818 μs
permute2 | 7274339 μs | 5398051 μs
permute3 | 731976 μs | 44324 μs
template<Vector<Vector<int>> cycles>
void permute4(){
auto loop2 = [&]<std::size_t ...Js>(std::index_sequence<Js...>, auto I)
{
constexpr Vector<int> cycle = cycles[I];
int temp = arr[cycle[0]];
((arr[cycle[Js]] = arr[cycle[Js+1]]), ...);
arr[cycle[sizeof...(Js)]] = temp;
};
[&]<std::size_t ...Is>(std::index_sequence<Is...>)
{
(loop2(std::make_index_sequence<cycles[Is].size() - 1>(),
std::integral_constant<std::size_t, Is>{}),
...);
}(std::make_index_sequence<cycles.size()>());
}