Are std::fill, std::copy specialized for std::vector?

后端 未结 4 1917
一整个雨季
一整个雨季 2020-12-31 07:14

When thinking about this question I start to wondering if std::copy() and/or std::fill are specialized (I really mean optimized) for std::vec

4条回答
  •  無奈伤痛
    2020-12-31 07:58

    There is no specialization for it, but you can still use it. (even though it's slow)

    But here is a trick I found which enables std::fill on std::vector, using proxy class std::_Vbase.

    (WARNING: I've tested it only for MSVC2013, so it may not work on other compilers.)

    int num_bits = 100000;
    std::vector bit_set(num_bits , true);
    
    int bitsize_elem = sizeof(std::_Vbase) * 8; // 1byte = 8bits
        
    int num_elems = static_cast(std::ceil(num_bits / static_cast(bitsize_elem)));
    

    Here, since you need the whole bits of an element if you use any bit of it, the number of elements must be rounded up.

    Using this information, we will build a vector of pointers that pointing the original elements underlying the bits.

    std::vector elem_ptrs(num_elems, nullptr);
    
    std::vector::iterator bitset_iter = bit_set.begin();
    for (int i = 0; i < num_elems; ++i)
    {
        std::_Vbase* elem_ptr = const_cast((*bitset_iter)._Myptr);
        elem_ptrs[i] = elem_ptr;
        std::advance(bitset_iter, bitsize_elem);
    }
    

    (*bitset_iter)._Myptr : By dereferencing the iterator of std::vector, you can access the proxy class reference and its member _Myptr.

    Since the return type of std::vector::iterator::operator*() is const std::_Vbase*, remove the constness of it by const_cast.

    Now we get the pointer which is pointing the original element underlying those bits, std::_Vbase* elem_ptr.

    elem_ptrs[i] = elem_ptr : Record this pointer,...

    std::advance(bitset_iter, bitsize_elem) : ...and continue our journey to find the next element, by jumping bits held by the previous element.

    std::fill(elem_ptrs[0], elem_ptrs[0] + num_elems, 0); // fill every bits "false"
    std::fill(elem_ptrs[0], elem_ptrs[0] + num_elems, -1); // fill every bits "true"
    

    Now, we can use std::fill on the vector of pointers, rather than vector of bits.

    Perhaps some may feel uncomfortable using the proxy class externally and even remove the constness of it.

    But if you don't care about that and want something fast, this is the fastest way.

    I did some comparisons below. (made new project, nothing changed config, release, x64)

    int it_max = 10; // do it 10 times ...
    int num_bits = std::numeric_limits::max(); // 2147483647
    
    std::vector bit_set(num_bits, true);
    for (int it_count = 0; it_count < it_max; ++it_count)
    {
        std::fill(elem_ptrs[0], elem_ptrs[0] + num_elems, 0);
    } // Elapse Time : 0.397sec
    
    for (int it_count = 0; it_count < it_max; ++it_count)
    {
        std::fill(bit_set.begin(), bit_set.end(), false);
    } // Elapse Time : 18.734sec
    
    for (int it_count = 0; it_count < it_max; ++it_count)
    {
        for (int i = 0; i < num_bits; ++i)
        {
            bit_set[i] = false;
        }
    } // Elapse Time : 21.498sec
    
    for (int it_count = 0; it_count < it_max; ++it_count)
    {
        bit_set.assign(num_bits, false);
    } // Elapse Time : 21.779sec
    
    for (int it_count = 0; it_count < it_max; ++it_count)
    {
        bit_set.swap(std::vector(num_bits, false)); // You can not use elem_ptrs anymore
    } // Elapse Time : 1.3sec
    

    There is one caveat. When you swap() the original vector with another one, then the vector of pointers becomes useless!

提交回复
热议问题