Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/fortran/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 改进值散列_C++_Optimization - Fatal编程技术网

C++ 改进值散列

C++ 改进值散列,c++,optimization,C++,Optimization,目前我有以下几点: class Transform { int N; // set in other functions std::unordered_map<int,float> cache; float Wn(int n) { std::unordered_map<int,float>::const_iterator got = cache.find(n); if(got == cache.end()

目前我有以下几点:

class Transform
{
    int N; // set in other functions

    std::unordered_map<int,float> cache;

    float Wn(int n)
    {
        std::unordered_map<int,float>::const_iterator got = cache.find(n);
        if(got == cache.end())
        return cache[n] = sin((M_PI / (2 * N)) * (n + 0.5f));

        return cache[n];
}
类转换
{
int N;//在其他函数中设置
std::无序映射缓存;
浮点数Wn(整数n)
{
std::无序映射::常量迭代器get=cache.find(n);
if(get==cache.end())
返回缓存[n]=sin((μPI/(2*n))*(n+0.5f));
返回缓存[n];
}

由于函数
Wn
被多次调用,并且只有
n
参数发生了更改,因此我尝试对其进行缓存。我的问题是,在许多情况下,函数需要的时间比没有缓存的情况长,有时甚至长25%。有没有办法对此进行优化?

假设输入整数通常在某个小范围内,只需使用a将数组作为缓存。即使某些值不能缓存,也比哈希更有效。

假设输入整数通常在某个小范围内,只需使用数组作为缓存。即使某些值不能缓存,也比哈希更有效。

我仔细查看了您的代码,以及可以做些什么来改进我喜欢它

下面是函数wN()的3个带注释的版本:

在这种情况下,这并不重要(优化者负责冗余查找),有一种更符合习惯的方法来编写
Wn()

首先是原文:

float original_cached_wN(int n)
{
    // compute hash and search
    std::unordered_map<int,float>::const_iterator got = cache.find(n);
    if(got == cache.end())
        // recompute hash
        // search again
        // default construct 
        // overwrite
        return cache[n] = compute_wN(n);

    // recompute hash
    // search again
    // default construct 
    // overwrite
    return cache[n];
}
最后,只需计算Wn而无需缓存:

float compute_wN(int n) const
{
    return sin((M_PI / (2 * N)) * (n + 0.5f));
}
下面是一个测试程序,可以查看由以下3种方法生成的编译源代码:

#include <iostream>
#include <cmath>
#include <unordered_map>
#include <sstream>
#include <vector>

class Transform
{

    int N;

    std::unordered_map<int,float> cache;

public:
    Transform(int N) : N(N) {}

    float original_cached_wN(int n)
    {
        std::unordered_map<int,float>::const_iterator got = cache.find(n);
        if(got == cache.end())
            return cache[n] = compute_wN(n);

        return cache[n];
    }

    float improved_cached_wN(int n)
    {
        std::unordered_map<int,float>::const_iterator got = cache.find(n);
        if(got == cache.end())
        {
            got = cache.emplace(n, compute_wN(n)).first;
        }
        return got->second;
    }

    float compute_wN(int n) const
    {
        return sin((M_PI / (2 * N)) * (n + 0.5f));
    }
};



int main()
{
    using namespace std;

    // this is to defeat the optimiser
    // and prefent compile-time evaluation of Wn
    std::istringstream ss ("5 4 6 7");
    int N = 10, n1 = 0, n2 = 1, n3 = 2;
    ss >> N >> n1 >> n2 >> n3;

    Transform t1(N);
    std::vector<float> v = {
        t1.original_cached_wN(n1),
        t1.improved_cached_wN(n2),
        t1.compute_wN(n3)
    };

    std::copy(v.begin(), v.end(), std::ostream_iterator<float>(cout, ", "));
    std::cout << std::endl;


    return 0;
}
查看编译后的输出,在我看来,搜索和更新映射的成本实际上超过了计算
W(n)

以下是apple clang 7在使用选项编译后为
compute\u wN()
发出的代码
-O3-march=native

movl    (%rdi), %eax
addl    %eax, %eax
vcvtsi2sdl  %eax, %xmm0, %xmm0
vmovsd  LCPI2_0(%rip), %xmm1    ## xmm1 = mem[0],zero
vdivsd  %xmm0, %xmm1, %xmm0
vcvtsi2ssl  %r15d, %xmm0, %xmm1
vaddss  LCPI2_1(%rip), %xmm1, %xmm1
vcvtss2sd   %xmm1, %xmm1, %xmm1
vmulsd  %xmm0, %xmm1, %xmm0
callq   _sin

老实说,与地图操作相比,它的代码要少得多。

我仔细研究了您的代码,以及可以做些什么来改进它

下面是函数wN()的3个带注释的版本:

在这种情况下,这并不重要(优化者负责冗余查找),有一种更符合习惯的方法来编写
Wn()

首先是原文:

float original_cached_wN(int n)
{
    // compute hash and search
    std::unordered_map<int,float>::const_iterator got = cache.find(n);
    if(got == cache.end())
        // recompute hash
        // search again
        // default construct 
        // overwrite
        return cache[n] = compute_wN(n);

    // recompute hash
    // search again
    // default construct 
    // overwrite
    return cache[n];
}
最后,只需计算Wn而无需缓存:

float compute_wN(int n) const
{
    return sin((M_PI / (2 * N)) * (n + 0.5f));
}
下面是一个测试程序,可以查看由以下3种方法生成的编译源代码:

#include <iostream>
#include <cmath>
#include <unordered_map>
#include <sstream>
#include <vector>

class Transform
{

    int N;

    std::unordered_map<int,float> cache;

public:
    Transform(int N) : N(N) {}

    float original_cached_wN(int n)
    {
        std::unordered_map<int,float>::const_iterator got = cache.find(n);
        if(got == cache.end())
            return cache[n] = compute_wN(n);

        return cache[n];
    }

    float improved_cached_wN(int n)
    {
        std::unordered_map<int,float>::const_iterator got = cache.find(n);
        if(got == cache.end())
        {
            got = cache.emplace(n, compute_wN(n)).first;
        }
        return got->second;
    }

    float compute_wN(int n) const
    {
        return sin((M_PI / (2 * N)) * (n + 0.5f));
    }
};



int main()
{
    using namespace std;

    // this is to defeat the optimiser
    // and prefent compile-time evaluation of Wn
    std::istringstream ss ("5 4 6 7");
    int N = 10, n1 = 0, n2 = 1, n3 = 2;
    ss >> N >> n1 >> n2 >> n3;

    Transform t1(N);
    std::vector<float> v = {
        t1.original_cached_wN(n1),
        t1.improved_cached_wN(n2),
        t1.compute_wN(n3)
    };

    std::copy(v.begin(), v.end(), std::ostream_iterator<float>(cout, ", "));
    std::cout << std::endl;


    return 0;
}
查看编译后的输出,在我看来,搜索和更新映射的成本实际上超过了计算
W(n)

以下是apple clang 7在使用选项编译后为
compute\u wN()
发出的代码
-O3-march=native

movl    (%rdi), %eax
addl    %eax, %eax
vcvtsi2sdl  %eax, %xmm0, %xmm0
vmovsd  LCPI2_0(%rip), %xmm1    ## xmm1 = mem[0],zero
vdivsd  %xmm0, %xmm1, %xmm0
vcvtsi2ssl  %r15d, %xmm0, %xmm1
vaddss  LCPI2_1(%rip), %xmm1, %xmm1
vcvtss2sd   %xmm1, %xmm1, %xmm1
vmulsd  %xmm0, %xmm1, %xmm0
callq   _sin


老实说,这比映射操作的代码要少得多。

实际上它们可以在
SHORT\u MIN
SHORT\u MAX
的范围内,而且有几个根本不会出现,所以我会消耗不必要的RAM。另外,为了检查值是否已经计算过,我需要另一个布尔数组,增加内存,采用这种表格形式e用于早期的电脑游戏,预先计算了所有可能的值,然后只是查找。@如果一个表中有2^16个条目真的没有那么多,那么您最好预先计算所有的值。2^16只有65k,几乎什么都没有。@Tofife:您不需要布尔数组来知道计算了哪些值。只需在数组中填入al即可启动时的L65536值,或者如果您不喜欢,用NAN填充它,并将其用作未缓存输入的标记。实际上,它们可以在
SHORT_MIN
SHORT_MAX
的范围内,并且一些值根本不会出现,因此我将消耗不必要的RAM。此外,要检查该值是否已经计算过,我还需要另一个boolean数组,增加内存。这种形式的表用于早期的电脑游戏,预先计算所有可能的值,然后再查找。@如果一个包含2^16个条目的表真的没有那么多,那么您最好预先计算它的所有值2^16只有65k,几乎什么都没有。@toffe:您不需要布尔数组来知道是哪个值已经计算了h个。只需在启动时用所有65536个值填充数组,或者如果您不喜欢,用NAN填充数组,并将其用作未缓存输入的标记。它调用
sin
,但您似乎忽略了这一点。@harold我想您必须对代码进行基准测试,以了解计算正弦是否更为昂贵在现实中,nsive比(例如)搜索不在缓存中的地图更有效。是的,但我的意思是,你的结论是它“代码更少”…也许吧,但它有一个调用,所以得出的结论真的很奇怪。它可能隐藏了任意多的代码。@harold从内存中一个64位的奔腾需要大约350个周期来计算一个sin。它会这样做,而不需要接触任何内存。它调用
sin
,不过,你似乎忽略了这一点。@harold我想你必须对代码进行加密,以了解计算正弦在现实中是否比(例如)搜索不在缓存中的地图更昂贵。是的,但我的意思是,你的结论是它“代码更少”…也许吧,但它有一个调用,所以得出的结论真的很奇怪。它可能隐藏了任意多的代码。@harold从内存中读取64位奔腾大约需要350个周期来计算一个sin。它不需要触摸任何内存就可以做到这一点。