代码之家  ›  专栏  ›  技术社区  ›  Kirill V. Lyadvinsky

如何用“int32”值快速填充内存?

  •  11
  • Kirill V. Lyadvinsky  · 技术社区  · 14 年前

    是否有一个函数(ssex intrinsics is ok)将用指定的 int32_t 价值?例如,当该值等于 0xAABBCC00 结果内存应该如下所示:

    AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
    AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
    AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
    AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
    ...
    

    我可以用 std::fill 或者简单的循环,但速度不够快。


    在程序开始时只执行一次矢量调整,这不是问题。瓶颈正在填满内存。

    简化代码:

    struct X
    {
      typedef std::vector<int32_t> int_vec_t;
      int_vec_t buffer;
    
      X() : buffer( 5000000 ) { /* some more action */ }
      ~X() { /* some code here */ }
    
      // the following function is called 25 times per second
      const int_vec_t& process( int32_t background, const SOME_DATA& data );
    };
    
    const X::int_vec_t& X::process( int32_t background, const SOME_DATA& data )
    {
        // the following one string takes 30% of total time of #process function
        std::fill( buffer.begin(), buffer.end(), background );
    
        // some processing
        // ...
    
        return buffer;
    }
    
    9 回复  |  直到 9 年前
        1
  •  9
  •   wj32    14 年前

    我就是这样做的(请原谅微软的态度):

    VOID FillInt32(__out PLONG M, __in LONG Fill, __in ULONG Count)
    {
        __m128i f;
    
        // Fix mis-alignment.
        if ((ULONG_PTR)M & 0xf)
        {
            switch ((ULONG_PTR)M & 0xf)
            {
                case 0x4: if (Count >= 1) { *M++ = Fill; Count--; }
                case 0x8: if (Count >= 1) { *M++ = Fill; Count--; }
                case 0xc: if (Count >= 1) { *M++ = Fill; Count--; }
            }
        }
    
        f.m128i_i32[0] = Fill;
        f.m128i_i32[1] = Fill;
        f.m128i_i32[2] = Fill;
        f.m128i_i32[3] = Fill;
    
        while (Count >= 4)
        {
            _mm_store_si128((__m128i *)M, f);
            M += 4;
            Count -= 4;
        }
    
        // Fill remaining LONGs.
        switch (Count & 0x3)
        {
            case 0x3: *M++ = Fill;
            case 0x2: *M++ = Fill;
            case 0x1: *M++ = Fill;
        }
    }
    
        2
  •  6
  •   Mark B    14 年前

    我不得不问:你有没有明确的介绍 std::fill 并证明它是性能瓶颈?我猜想它是以一种非常有效的方式实现的,这样编译器就可以自动生成适当的指令(例如 -march 在GCC上。

    如果这是瓶颈,仍然可以从算法重新设计(如果可能)中获得更好的好处,以避免设置太多的内存(显然是反复设置),从而使您使用的填充机制不再重要。

        3
  •  4
  •   Community Bayu Bramantya    7 年前

    谢谢大家的回答。我检查过了 wj32's solution ,但显示的时间与 std::fill 做。我当前的解决方案的工作速度(在Visual Studio 2008中)是 性病::填写 在功能的帮助下 memcpy :

     // fill the first quarter by the usual way
     std::fill(buffer.begin(), buffer.begin() + buffer.size()/4, background);
     // copy the first quarter to the second (very fast)
     memcpy(&buffer[buffer.size()/4], &buffer[0], buffer.size()/4*sizeof(background));
     // copy the first half to the second (very fast)
     memcpy(&buffer[buffer.size()/2], &buffer[0], buffer.size()/2*sizeof(background));
    

    在生产代码中,需要添加检查 buffer.size() 可被4整除并为此添加适当的处理。

        4
  •  3
  •   wheaties    14 年前

    你考虑过使用吗

    vector<int32_t> myVector;
    myVector.reserve( sizeIWant );
    

    然后使用std::fill?或者可能是 std::vector 哪个参数将保留的项数和初始化它们的值作为参数?

        5
  •  0
  •   Viktor Sehr    14 年前

    假设您的背景参数中有有限的值(或者更好的是,只在上面),也许您应该尝试分配一个静态向量,并简单地使用memcpy。

    const int32_t sBackground = 1234;
    static vector <int32_t> sInitalizedBuffer(n, sBackground);
    
        const X::int_vec_t& X::process( const SOME_DATA& data )
        {
            // the following one string takes 30% of total time of #process function
            std::memcpy( (void*) data[0], (void*) sInitalizedBuffer[0], n * sizeof(sBackground));
    
            // some processing
            // ...
    
            return buffer;
        }
    
        6
  •  0
  •   5ound    14 年前

    我刚刚测试了std::用g++填充完整的优化(sse等)。启用):

    #include <algorithm>
    #include <inttypes.h>
    
    int32_t a[5000000];
    
    int main(int argc,char *argv[])
    {
        std::fill(a,a+5000000,0xAABBCC00);
        return a[3];
    }
    

    内环看起来像:

    L2:
        movdqa  %xmm0, -16(%eax)
        addl    $16, %eax
        cmpl    %edx, %eax
        jne L2
    

    看起来0xaabbcc00 x 4已加载到xmm0中,一次移动16个字节。

        7
  •  0
  •   phuclv    9 年前

    不完全确定如何在一行中设置4个字节,但是如果您希望在一次重写中只填充一个字节,则可以使用 memset .

    void * memset ( void * ptr, int value, size_t num );
    

    填充内存块

    设置内存块的第一个num字节 ptr 到指定值(解释为 unsigned char )

        8
  •  0
  •   acui    9 年前

    VS2013和VS2015可以将普通for循环优化为 rep stos 指令。这是填充缓冲区的最快方法。您可以指定 std::fill 对于您喜欢的类型:

    namespace std {
        inline void fill(vector<int>::iterator first, vector<int>::iterator last, int value){
            for (size_t i = 0; i < last - first; i++)
                first[i] = value;
        }
    }
    

    顺便说一句,要让编译器进行优化,缓冲区必须由下标运算符访问。

    它不会在GCC和Clang上工作。它们都将把代码编译成条件跳转循环。它跑得和原来一样慢 性病::填写 . 虽然 wchar_t 是32位的 wmemset 没有像这样的装配工具 memset . 所以您必须编写汇编代码来进行优化。

        9
  •  -2
  •   Jay    14 年前

    它可能有点不可移植,但您可以使用重叠的内存副本。 用您想要的模式填充前四个字节,然后使用memcpy()。

    int32* p = (int32*) malloc( size );
    *p = 1234;
    memcpy( p + 4, p, size - 4 );
    

    别以为你能快得多