Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/153.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++ 使用向量<;char>;作为缓冲区,而不在resize()上初始化它_C++_Boost_C++11 - Fatal编程技术网

C++ 使用向量<;char>;作为缓冲区,而不在resize()上初始化它

C++ 使用向量<;char>;作为缓冲区,而不在resize()上初始化它,c++,boost,c++11,C++,Boost,C++11,我想使用vector作为缓冲区。该接口非常适合我的需要,但是当它的大小超过当前大小时,会有性能损失,因为内存已经初始化。我不需要初始化,因为数据在任何情况下都会被一些第三方C函数覆盖。是否有方法或特定的分配器来避免初始化步骤?请注意,我确实希望使用resize(),而不是像reserve()和capacity()这样的技巧,因为我需要size()始终表示我的“缓冲区”在任何时刻的有效大小,而capacity()可能在resize()之后大于它的大小,所以,同样,我不能依赖capacity()作为

我想使用
vector
作为缓冲区。该接口非常适合我的需要,但是当它的大小超过当前大小时,会有性能损失,因为内存已经初始化。我不需要初始化,因为数据在任何情况下都会被一些第三方C函数覆盖。是否有方法或特定的分配器来避免初始化步骤?请注意,我确实希望使用
resize()
,而不是像
reserve()
capacity()
这样的技巧,因为我需要
size()
始终表示我的“缓冲区”在任何时刻的有效大小,而
capacity()
可能在
resize()
之后大于它的大小,所以,同样,我不能依赖
capacity()
作为我的应用程序的重要信息。此外,向量的(新)大小事先是未知的,因此我不能使用
std::array
。如果vector不能以这种方式配置,我想知道我可以使用什么样的容器或分配器来代替
vector
。唯一的要求是,矢量的替代方案最多只能基于STL或Boost。我可以访问C++11。

标准库中没有满足您要求的内容,我对boost中的内容也一无所知

我可以想到三个合理的选择:

  • 现在请坚持使用
    std::vector
    ,在代码中留下注释,如果这会导致应用程序出现瓶颈,请返回注释
  • 使用带有空
    构造
    /
    销毁
    方法的自定义分配器-希望您的优化程序足够聪明,能够删除对它们的任何调用
  • 围绕动态分配的数组创建包装器,只实现所需的最小功能
    • 封装它

      将其初始化为最大大小(非保留)

      保留对表示实际大小结尾的迭代器的引用,如您所说


      对于您的算法,请使用
      begin
      real end
      ,而不是
      end

      已知的问题是,即使对于
      std::vector
      ,也无法明确关闭初始化

      人们通常实现自己的
      pod_vector
      ,而不进行任何元素初始化

      另一种方法是创建与char布局兼容的类型,其构造函数不执行任何操作:

      struct NoInitChar
      {
          char value;
          NoInitChar() noexcept {
              // do nothing
              static_assert(sizeof *this == sizeof value, "invalid size");
              static_assert(__alignof *this == __alignof value, "invalid alignment");
          }
      };
      
      int main() {
          std::vector<NoInitChar> v;
          v.resize(10); // calls NoInitChar() which does not initialize
      
          // Look ma, no reinterpret_cast<>!
          char* beg = &v.front().value;
          char* end = beg + v.size();
      }
      
      struct NoInitChar
      {
      字符值;
      NoInitChar()noexcept{
      //无所事事
      静态_断言(sizeof*this==sizeof值,“无效大小”);
      static_assert(uuu alignof*this==uuu alignof value,“无效对齐”);
      }
      };
      int main(){
      std::向量v;
      v、 调整大小(10);//调用不初始化的NoInitChar()
      //听着,妈,不要再解释了!
      char*beg=&v.front().value;
      char*end=beg+v.size();
      }
      
      作为一种与不同荚果类型的载体一起工作的载体:

      template<typename V>
      void resize(V& v, size_t newSize)
      {
          struct vt { typename V::value_type v; vt() {}};
          static_assert(sizeof(vt[10]) == sizeof(typename V::value_type[10]), "alignment error");
          typedef std::vector<vt, typename std::allocator_traits<typename V::allocator_type>::template rebind_alloc<vt>> V2;
          reinterpret_cast<V2&>(v).resize(newSize);
      }
      

      因此,总结一下stackoverflow上的各种解决方案:

    • 使用特殊的默认初始分配器。()
      缺点:将向量类型更改为
      std::vector vec
    • 在具有空构造函数的字符周围使用包装结构
      struct NoInitChar
      ,从而跳过值初始化()
      缺点:将向量类型更改为
      std::vector vec
    • 向量
      临时转换为
      向量
      并调整其大小()
      缺点:不会更改向量的类型,但您需要调用
      您的_resize_函数(vec,x)
      而不是
      vec.resize(x)

    • 在这篇文章中,我想指出,所有这些方法都需要由编译器进行优化,以提高程序的速度。我可以确认,在调整大小时,新字符的初始化确实在我测试的每个编译器中都得到了优化。所以一切看起来都很好…
      但是-->由于方法1和2改变了向量的类型,在更“复杂”的情况下使用这些向量会发生什么情况。
      考虑这个例子:

      #include <time.h>
      #include <vector>
      #include <string_view>
      #include <iostream>
      
      //high precision-timer
      double get_time () {
          struct timespec timespec;
          ::clock_gettime (CLOCK_MONOTONIC_RAW, &timespec);
          return timespec.tv_sec + timespec.tv_nsec / (1000.0 * 1000.0 * 1000.0);
      }
      
      //method 1 --> special allocator
      //reformated to make it readable
      template <typename T, typename A = std::allocator<T>>
      class default_init_allocator : public A {
      private:
          typedef std::allocator_traits<A> a_t;
      public:
          template<typename U>
          struct rebind {
              using other = default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
          };
          using A::A;
      
          template <typename U>
          void construct (U* ptr) noexcept (std::is_nothrow_default_constructible<U>::value) {
              ::new (static_cast<void*>(ptr)) U;
          }
          template <typename U, typename...Args>
          void construct (U* ptr, Args&&... args) {
              a_t::construct (static_cast<A&>(*this), ptr, std::forward<Args>(args)...);
          }
      };
      
      //method 2 --> wrapper struct
      struct NoInitChar {
      public:
          NoInitChar () noexcept { }
          NoInitChar (char c) noexcept : value (c) { }
      public:
          char value;
      };
      
      //some work to waste time
      template<typename T>
      void do_something (T& vec, std::string_view str) {
          vec.push_back ('"');
          vec.insert (vec.end (), str.begin (), str.end ());
          vec.push_back ('"');
          vec.push_back (',');
      }
      
      int main (int argc, char** argv) {
          double timeBegin = get_time ();
      
          std::vector<char> vec;                                 //normal case
          //std::vector<char, default_init_allocator<char>> vec; //method 1
          //std::vector<NoInitChar> vec;                         //method 2
          vec.reserve (256 * 1024 * 1024);
          for (int i = 0; i < 1024 * 1024; ++i) {
              do_something (vec, "foobar1");
              do_something (vec, "foobar2");
              do_something (vec, "foobar3");
              do_something (vec, "foobar4");
              do_something (vec, "foobar5");
              do_something (vec, "foobar6");
              do_something (vec, "foobar7");
              do_something (vec, "foobar8");
              vec.resize (vec.size () + 64);
          }
      
          double timeEnd = get_time ();
          std::cout << (timeEnd - timeBegin) * 1000 << "ms" << std::endl;
          return 0;
      }
      

      这是非常罕见的,你会需要这样做;我强烈建议您对您的情况进行基准测试,以绝对确保此黑客是必要的

      即使如此,我还是更喜欢NoInitChar解决方案。(见马克西姆的答案)

      <>但是如果你确信你会从中受益,而NoInitChar不为你工作,你使用CLAN,或者GCC,或者MSVC作为编译器,考虑使用愚蠢的程序来实现这个目的。

      基本思想是,每个库实现都有一个例程,用于执行未初始化的大小调整;你只要打电话就行了


      当你知道脸谱网的C++代码依赖于这个黑客操作时,如果这些库实现的新版本需要它,那么他们会更新它。 <代码>保留<代码> >代码>容量<代码>或
      std::array
      ,或适当的构造函数..不太清楚您想要做什么/避免..哪种性能损失?你是怎么测量的?@UmNyobe不需要测量。很明显,在最坏的情况下,从0调整到10^9会降低性能,即使分配器足够聪明,可以使用memset(这是无法保证的)。@us2012不能保证容量()与大小相同。@us2012:使用向量的保留存储不是一个好主意。如果向量的内部存储大小发生变化,“伪”元素的值将不会被复制,带有选中迭代器/
      运算符[]
      的向量实现将断言您是否使用该函数访问“伪”元素等+1第二项是大希望,但如果优化器相当智能,它可能会看到曙光。第三个显然授予了最多的控制权,但代价是要维护更多的代码(但它真的会比定制的
      test():
              push    rbx
              mov     edi, 1000
              call    operator new(unsigned long)
              mov     rbx, rax
              mov     edx, 1000
              mov     rdi, rax
              xor     esi, esi
              call    memset
              mov     rdi, rbx
              pop     rbx
              jmp     operator delete(void*)
      
      test_noinit():
              push    rax
              mov     edi, 1000
              call    operator new(unsigned long)
              mov     rdi, rax
              pop     rax
              jmp     operator delete(void*)
      
      #include <time.h>
      #include <vector>
      #include <string_view>
      #include <iostream>
      
      //high precision-timer
      double get_time () {
          struct timespec timespec;
          ::clock_gettime (CLOCK_MONOTONIC_RAW, &timespec);
          return timespec.tv_sec + timespec.tv_nsec / (1000.0 * 1000.0 * 1000.0);
      }
      
      //method 1 --> special allocator
      //reformated to make it readable
      template <typename T, typename A = std::allocator<T>>
      class default_init_allocator : public A {
      private:
          typedef std::allocator_traits<A> a_t;
      public:
          template<typename U>
          struct rebind {
              using other = default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
          };
          using A::A;
      
          template <typename U>
          void construct (U* ptr) noexcept (std::is_nothrow_default_constructible<U>::value) {
              ::new (static_cast<void*>(ptr)) U;
          }
          template <typename U, typename...Args>
          void construct (U* ptr, Args&&... args) {
              a_t::construct (static_cast<A&>(*this), ptr, std::forward<Args>(args)...);
          }
      };
      
      //method 2 --> wrapper struct
      struct NoInitChar {
      public:
          NoInitChar () noexcept { }
          NoInitChar (char c) noexcept : value (c) { }
      public:
          char value;
      };
      
      //some work to waste time
      template<typename T>
      void do_something (T& vec, std::string_view str) {
          vec.push_back ('"');
          vec.insert (vec.end (), str.begin (), str.end ());
          vec.push_back ('"');
          vec.push_back (',');
      }
      
      int main (int argc, char** argv) {
          double timeBegin = get_time ();
      
          std::vector<char> vec;                                 //normal case
          //std::vector<char, default_init_allocator<char>> vec; //method 1
          //std::vector<NoInitChar> vec;                         //method 2
          vec.reserve (256 * 1024 * 1024);
          for (int i = 0; i < 1024 * 1024; ++i) {
              do_something (vec, "foobar1");
              do_something (vec, "foobar2");
              do_something (vec, "foobar3");
              do_something (vec, "foobar4");
              do_something (vec, "foobar5");
              do_something (vec, "foobar6");
              do_something (vec, "foobar7");
              do_something (vec, "foobar8");
              vec.resize (vec.size () + 64);
          }
      
          double timeEnd = get_time ();
          std::cout << (timeEnd - timeBegin) * 1000 << "ms" << std::endl;
          return 0;
      }
      
                      g++ 7.5.0   g++ 8.4.0   g++ 9.3.0   clang++ 9.0.0
      vector<char>         95ms       134ms       133ms            97ms
      method 1            130ms       159ms       166ms            91ms
      method 2            166ms       160ms       159ms            89ms
      
      $(cc) -O3 -flto -std=c++17 sample.cpp