【问题标题】:Initializing a C++ vector to random values... fast将 C++ 向量初始化为随机值...快速
【发布时间】:2011-02-21 02:04:00
【问题描述】:

嘿,我想尽可能快地做到这一点,因为它在我正在编写的程序中被调用了很多,所以有没有比将 C++ 向量初始化为随机值更快的方法:

double range;//set to the range of a particular function i want to evaluate.
std::vector<double> x(30, 0.0);
for (int i=0;i<x.size();i++) {
    x.at(i) = (rand()/(double)RAND_MAX)*range;
}

编辑:修复 x 的初始化程序。

【问题讨论】:

  • 按索引访问向量数据元素的正确类型是std::vector&lt;...&gt;::size_type,而不是int
  • 我想常见的编译器会在他们的优化器中将range/RAND_MAX移出循环?
  • 很想知道分析器是否确实将其识别为问题。

标签: c++ optimization vector random


【解决方案1】:
int main() {
  int size = 10;
  srand(time(NULL));
  std::vector<int> vec(size);
  std::generate(vec.begin(), vec.end(), rand);

  std::vector<int> vec_2(size);
  std::generate(vec_2.begin(), vec_2.end(), [](){ return rand() % 50;})
}

需要包含向量、算法、时间、cstdlib。

【讨论】:

    【解决方案2】:

    我使用 Jerry Coffin 的函子方法已经有一段时间了,但是随着 C++11 的到来,我们有了很多很酷的新随机数功能。要使用随机的float 值填充数组,我们现在可以执行以下操作。 . .

    const size_t elements = 300;
    std::vector<float> y(elements);    
    std::uniform_real_distribution<float> distribution(0.0f, 2.0f); //Values between 0 and 2
    std::mt19937 engine; // Mersenne twister MT19937
    auto generator = std::bind(distribution, engine);
    std::generate_n(y.begin(), elements, generator); 
    

    查看Wikipedia的相关部分了解更多引擎和发行版

    【讨论】:

      【解决方案3】:

      现在,这应该真的快,因为循环不会执行。

      就个人而言,我可能会使用这样的东西:

      struct gen_rand { 
          double range;
      public:
          gen_rand(double r=1.0) : range(r) {}
          double operator()() { 
              return (rand()/(double)RAND_MAX) * range;
          }
      };
      
      std::vector<double> x(num_items);
      std::generate_n(x.begin(), num_items, gen_rand());
      

      编辑:这纯粹是一种微优化,可能根本没有任何区别,但您可以考虑重新安排计算以获得类似的结果:

      struct gen_rand { 
          double factor;
      public:
          gen_rand(double r=1.0) : factor(range/RAND_MAX) {}
          double operator()() { 
              return rand() * factor;
          }
      };
      

      当然,编译器很有可能已经这样做了(或类似的东西),但无论如何尝试它都不会有坏处(尽管它实际上只可能有助于关闭优化)。

      Edit2:“sbi”(通常情况下)是正确的:您可能会通过最初保留空间,然后使用插入迭代器将数据放置到位来获得一点:

      std::vector<double> x;
      x.reserve(num_items);
      std::generate_n(std::back_inserter(x), num_items, gen_rand());
      

      和以前一样,我们正在进行这样的微观优化,我完全不确定我是否真的期望看到不同之处。特别是,由于这一切都是使用模板完成的,因此很有可能大多数(如果不是全部)代码将内联生成。在这种情况下,优化器可能会注意到初始数据全部被覆盖,并跳过对其进行初始化。

      然而,最后,几乎唯一真正可能产生重大影响的部分是删除.at(i)。其他人可能,但启用优化后,我真的不希望他们这样做。

      【讨论】:

      • 不公平,我先看到了。我只是太慢了,你比我快。
      • 这仍然使用 0.0 值预初始化向量,然后覆盖这些值。 reserve() and and insert 迭代器不会消除这种情况吗?
      • @Wheaties:这一定是第一次——我的(糟糕的)打字,通常是相反的! :-)
      • 杰瑞,我把我所说的表述为一个问题,因为我不确定。我可以想象插入器迭代器的额外开销比一些内置函数的初始初始化花费的时间更长,特别是如果不是所有的前者都可以内联,而后者被优化为 std::memset(),实现为 CPU 内在函数。我想最后你只需要测量。 但除非应用程序在该循环中花费 20% 的时间,否则即使您将其速度加倍,也不会有人注意到。
      • @sbi:这就是为什么我说它在我的编辑中是一个微优化,并且不确定它会有什么好处。尽管如此,在这种情况下,我猜测迭代器是寄存器中的指针,因此递增它几乎没有开销。除非初始化被完全优化掉,否则它涉及写入内存,并且您可以在寄存器与一个内存事务一样昂贵之前增加一个 lot。然而,最后,你是对的:只有分析才能确定,而且它几乎不可能很重要。
      【解决方案4】:

      我对这些问题的看法是一种橡胶与道路相结合的方法。
      换句话说,有些事情必须发生,不能绕开,例如:

      • rand() 函数必须被调用 N 次。

      • rand() 的结果必须转换为双精度然后乘以某个值。

      • 结果数字必须存储在数组的连续元素中。

      对象至少要完成这些事情。

      其他问题,例如是否使用std::vector 和迭代器都可以,只要它们不添加任何额外的循环即可。 查看它们是否增加了大量额外循环的最简单方法是在汇编语言级别单步执行代码。

      【讨论】:

        【解决方案5】:

        @Jerry Coffin 的回答看起来很不错。不过还有两个想法:

        1. 内联 - 您的所有向量访问都将非常快,但如果对 rand() 的调用是外联的,则函数调用开销可能会占主导地位。如果是这种情况,您可能需要创建自己的 pseudorandom number generator

        2. SIMD - 如果您要推出自己的 PRNG,不妨让它一次计算 2 个双精度数(或 4 个浮点数)。这将减少整数到浮点数的转换以及乘法。我从未尝试过,但显然有一个非常好的 Mersenne Twister 的 SIMD 版本。一个简单的linear congruential generator 也可能足够好(这可能是 rand() 已经在使用的)。

        【讨论】:

          【解决方案6】:

          是的,而 x.at(i) 进行边界检查,而 x[i] 不这样做。 另外,您的代码不正确,因为您没有提前指定 x 的大小。您需要使用std::vector&lt;double&gt; x(n),其中n是您要使用的元素数;否则,您的循环将永远不会执行。

          或者,您可能想要创建一个自定义迭代器来生成随机值并使用迭代器填充它;因为 std::vector 构造函数无论如何都会初始化它的元素,所以如果你有一个生成随机值的自定义迭代器类,你可能能够消除对项目的传递。

          在实现你自己的iterator 方面,这是我未经测试的代码:

           class random_iterator
           {
               public:
                   typedef std::input_iterator_tag iterator_category;
                   typedef double value_type;
                   typedef int difference_type;
                   typedef double* pointer;
                   typedef double& reference;
          
                   random_iterator() : _range(1.0), _count(0) {}
                   random_iterator(double range, int count) : 
                                                   _range(range), _count(count) {}
                   random_iterator(const random_iterator& o) : 
                                                   _range(o._range), _count(o._count) {}
                   ~random_iterator(){}
          
                   double operator*()const{ return ((rand()/(double)RAND_MAX) * _range); }
                   int operator-(const random_iterator& o)const{ return o._count-_count; }
                   random_iterator& operator++(){ _count--; return *this; }
                   random_iterator operator++(int){ random_iterator cpy(*this); _count--; return cpy; }
                   bool operator==(const random_iterator& o)const{ return _count==o._count; }
                   bool operator!=(const random_iterator& o)const{ return _count!=o._count; }
          
               private:
                   double _range;
                   int _count;
           };
          

          有了上面的代码,应该可以使用了:

          std::vector<double> x(random_iterator(range,number),random_iterator());
          

          也就是说,给定的其他解决方案的生成代码更简单,坦率地说,我只是明确地填充向量而不诉诸任何像这样的花哨的东西......但想想还是很酷的。

          【讨论】:

          • 好的。迭代器创建随机值的想法非常酷。
          • 哦,对不起,我只是在这里写的,它更多的是为了概念,但我会更正它,ty。你能给我一个例子或指导我到哪里可以阅读有关制作我自己的迭代器的信息吗?我对 C++ 很陌生。谢谢
          • @Flamewires,是的......您需要创建一个符合迭代器概念之一的类。请参阅:sgi.com/tech/stl/Iterators.html。如果您对 C++ 很陌生,那么创建自己的迭代器目前可能不是一个好主意,因为它涉及诸如“类型特征”、“模板”、“模板专业化”、“概念”和“运算符重载”。如果您熟悉这些,那么这样做可能是合理的。
          • @Michael:IMO,无论如何,这都是一个相当糟糕的主意。虽然可能将生成(伪)随机数视为对序列的迭代,但我认为这更有可能是误导而不是有用。
          • @Jerry:我不知道。这样的迭代器似乎与在 /dev/random 上使用输入迭代器相差无几,这是一个众所周知且已建立的 C++ 图标(仅在这种情况下失败,因为读取 /dev/random 永远不会到达 EOF)。跨度>
          【解决方案7】:
          #include <iostream>
          #include <vector>
          #include <algorithm>
          
          struct functor {
             functor(double v):val(v) {}
             double operator()() const {
                return (rand()/(double)RAND_MAX)*val;
             }
          private:
             double val;
          };
          
          int main(int argc, const char** argv) {
             const int size = 10;
             const double range = 3.0f;
          
             std::vector<double> dvec;
             std::generate_n(std::back_inserter(dvec), size, functor(range));
          
             // print all
             std::copy(dvec.begin(), dvec.end(), (std::ostream_iterator<double>(std::cout, "\n")));
          
             return 0;
          }
          

          опоздал :(

          【讨论】:

            【解决方案8】:

            您可以考虑使用以序列形式提供输出的伪随机数生成器。由于大多数 PRNG 无论如何都只是提供一个序列,这将比简单地一遍又一遍地调用 rand() 更有效。

            但是,我想我真的需要更多地了解你的情况。

            • 为什么这段代码执行这么多?您能否重组您的代码以避免如此频繁地重新生成随机数据?
            • 您的向量有多大?
            • 您的随机数生成器需要有多“好”?高质量分布的计算成本往往更高。
            • 如果您的向量很大,您是在重复使用它们的缓冲区空间,还是将其丢弃并重新分配到其他地方?随意创建新向量是破坏缓存的好方法。

            【讨论】:

              猜你喜欢
              • 2011-01-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-07-10
              相关资源
              最近更新 更多