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()>());
}