【问题标题】:Static variables and functions that are called once for each choice of arguments每次选择参数时调用一次的静态变量和函数
【发布时间】:2015-11-25 12:40:00
【问题描述】:

这是一个简单的 C++ 问题。

问题描述: 我有一个函数,它将一个整数作为输入,并返回一个长度为零的向量。假设我使用相同的参数多次调用该函数。我要避免的是我的函数每次调用时都会创建零向量。我希望这仅在第一次使用给定输入调用函数时发生。

我是如何处理它的: 这让我想到了静态变量。我想创建一个静态向量来保存每种大小所需的零向量,但无法弄清楚如何实现这一点。例如,我想要一些“看起来”像 [ [0], [0,0], ...] 的东西。

如果有其他方法可以解决此类问题,请随时分享!此外,我的向量示例有点专业,但更通用的回复(关于依赖于参数的静态变量)将不胜感激。

附带问题: 进一步概括,是否可以为每个参数选择定义一个仅调用一次的函数?

非常感谢。

【问题讨论】:

  • 使用 map 作为静态实例。
  • 向量的静态向量(或者更好的映射)是正确的想法,你只需要懒惰地初始化它。
  • 不清楚你在追求什么,你返回的向量是const吗?否则,无论如何,您每次都需要制作副本。除非复制速度比零初始化快,否则它可能是有意义的。并且不可能使每个参数集执行一次的函数,但是如果传递与以前相同的参数集,则可以创建一个什么都不做的函数。它会执行,但会进行检查并立即返回,或者提供缓存的结果等。
  • 这种技术称为“记忆化”。参见例如stackoverflow.com/questions/17805969/…
  • 我可以看到三种解释这个问题的方法。 1) 您需要一个按值返回vector 的函数,即您想要更改的副本。 2)您需要一个函数,该函数返回对您不需要更改的零向量的 const 引用。 3) 您需要一个函数,该函数返回对最初全零但可以更改的向量的非常量引用。显示您的代码可能会有所帮助。

标签: c++ static-members


【解决方案1】:

你可以有一张尺寸和矢量的地图,每个尺寸对应一个矢量:

#include <vector>
#include <map>
#include <cstddef>

std::vector<int>& get_vector(std::size_t size)
{
    static std::map<size_t, std::vector<int> >  vectors;
    std::map<size_t, std::vector<int> >::iterator iter = vectors.find(size);
    if (iter == vectors.end())
    {
        iter = vectors.insert(std::make_pair(size, std::vector<int>(size, 0))).first;
    }
    return iter->second;
}

【讨论】:

  • 你可以重复使用find/insert的结果,避免用operator[]重新找到
  • 请注意,这将返回一个非常量引用,这意味着调用者可以对映射中的向量进行更改,并且后续调用将不会获得零向量。这可能不是 OP 想要的。
【解决方案2】:

如果我正确理解您想要做什么,我认为您不会获得预期的好处。

我编写了一个快速基准来比较重复创建零向量的性能。第一个基准测试使用标准向量构造函数。第二个使用一个函数,它只在第一次创建向量并将其存储在地图中:

const std::vector<int>& zeros(std::size_t size) {
    static std::unordered_map<size_t, std::vector<int>> vectors;
    auto find = vectors.find(size);
    if (find != vectors.end())
        return find->second;
    auto insert = vectors.emplace(size, std::vector<int>(size));
    return insert.first->second;
}

std::chrono::duration<float> benchmarkUsingMap() {
  int sum = 0;
  auto start = std::chrono::high_resolution_clock::now();

  for (int i = 0; i != 10'000; ++i) {
    auto zeros10k = zeros(10'000);
    zeros10k[5342] = 1;
    sum += zeros10k[5342];    
  }

  auto end = std::chrono::high_resolution_clock::now();                      
  std::cout << "Sum: " << sum << "\n";
  return end - start;
}

std::chrono::duration<float> benchmarkWithoutUsingMap() {
  int sum = 0;
  auto start = std::chrono::high_resolution_clock::now();

  for (int i = 0; i != 10'000; ++i) {
    auto zeros10k = std::vector<int>(10'000);
    zeros10k[5342] = 1;
    sum += zeros10k[5342];    
  }

  auto end = std::chrono::high_resolution_clock::now();                                   
  std::cout << "Sum: " << sum << "\n";
  return end - start;
}                          

int main() {
  std::cout << "Benchmark without map: " << benchmarkWithoutUsingMap().count() << '\n';
  std::cout << "Benchmark using map: " << benchmarkUsingMap().count() << '\n';
}

输出:

Benchmark without map: 0.0188374
Benchmark using map: 0.134966

因此,在这种情况下,每次仅创建向量就快了近 10 倍。这是假设您要创建零向量的可变副本。

【讨论】:

  • 谢谢!确实,我对提高执行速度非常感兴趣,这就是我想避免重新创建零向量的原因。
【解决方案3】:

如果每个向量都需要是一个单独的实例,那么您必须为每个实例构建一个结构。由于您必须构建每个实例,因此您可以创建一个简单的 make_int_vector 函数,例如:

std::vector<int> make_int_vector(std::size_t size, int fill = 0) 
{ 
    return std::vector(size, fill); 
}

返回的向量将被移动或用copy elision 省略

【讨论】:

  • 这有意义吗?我的意思是与就地使用std::vector&lt;int&gt;(size) 有什么区别?
  • 这听起来很有用,但它回答了这个问题吗?
【解决方案4】:

您要求的是缓存。困难的部分是一个条目应该在缓存中存在多长时间。您当前的要求似乎是永久缓存,这意味着每个条目都将永远存在。对于这样一个简单的用例,一个静态地图就足够了:

template<typename T, typename U>
T cached(T (*funct)(U arg)) {
    static unordered_map<U, T> c;
    if (c.count(arg) == 0) {
        c[arg] = funct(arg);
    }
    return c[arg];
}

上面是返回一个值,这将需要一个副本。如果您想避免复制,只需返回一个引用,但是,如果您更改其中一个向量,下一次调用将返回修改后的值。

template<typename T, typename U>
&T cached(T (*funct)(U arg)) {
    static unordered_map<U, T> c;
    if (c.count(arg) == 0) {
        c[arg] = funct(arg);
    }
    return c[arg];
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-01
    相关资源
    最近更新 更多