std::exclusive_scan with execution policy does not work in-place - c++

cppreference says this for std::exclusive_scan:
d_first - the beginning of the destination range; may be equal to first
So there should be no problem with using std::exclusive_scan in "in-place" mode overwriting the storage. However, with the libstdc++ implementation that comes with GCC 12.2.0, it does not work with the overloads that use an execution policy, even if it is std::execution::seq. Consider this example:
#include <algorithm>
#include <numeric>
#include <execution>
#include <vector>
#include <cassert>
int main()
{
const int size = 10;
std::vector<int> vec(size);
// without execution policy
std::fill(vec.begin(), vec.end(), 1);
std::exclusive_scan(vec.begin(), vec.end(), vec.begin(), 0);
assert(vec[0] == 0); // the first element should be 0
assert(vec[size-1] == size-1); // the last element should be the sum
// sequential execution policy
std::fill(vec.begin(), vec.end(), 1);
std::exclusive_scan(std::execution::seq, vec.begin(), vec.end(), vec.begin(), 0);
assert(vec[0] == 0); // the first element should be 0
assert(vec[size-1] == size-1); // the last element should be the sum
// parallel execution policy
std::fill(vec.begin(), vec.end(), 1);
std::exclusive_scan(std::execution::par, vec.begin(), vec.end(), vec.begin(), 0);
assert(vec[0] == 0); // the first element should be 0
assert(vec[size-1] == size-1); // the last element should be the sum
}
See on godbolt: https://godbolt.org/z/Yvax1dz7e
Is this a bug in the cppreference documentation or libstdc++ implementation? It actually is possible to implement a parallel in-place exclusive scan algorithm.
I know there is Calculate prefix product with std::exclusive_scan and execution policy std::execution::par but it does not ask about a bug.

[exclusive.scan#8] of the standard explicitly says:
Remarks: result may be equal to first.
Also, latest MSVC, unlike GCC and Clang, accepts your code in C++17 mode: https://godbolt.org/z/78W9Wfbvh
So, this is a bug in libstdc++ implementation, while cppreference correctly represents standard's remark.

Related

Is it valid to call operator-- for an iterator when it points to std::begin()

Is it valid to call operator-- on an iterator that already points to the first element of the collection? Does the answer change for different collections (e.g. list vs vector vs set). E.g. see below
#include <algorithm>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>
int main()
{
std::vector<int> v {1, 2, 4, 8, 16};
auto i=v.begin();
auto j=i;
--i; // not sure what's the effect of this
v.erase(j);
++i; // also not sure if this is not std::begin() anymore, what's the effect of ++
v.erase(i);
// Print vector.
std::for_each(v.begin(), v.end(), [](const int n) { std::cout << n << ' '; });
}
I suspect it's undefined behaviour but not quite sure.
Furthermore, what about removing elements from a std::list like below
std::list<int> v = { 1, 2, 3, 4, 5, 6 };
for (auto it = v.begin(); it != v.end(); it++)
{
v.erase(it--);
}
Let's take std::list as an example, because essentially the same reasoning will apply to the other containers.
Looking at the member types of std::list, we see that std::list::iterator is a LegacyBidirectionalIterator. Checking the description there, we see the following precondition listed for operator-- to be valid:
Preconditions:
a is decrementable (there exists such b that a == ++b)
This is not the case for an iterator to the first element in a container, and indeed cppreference explicitly calls this out:
The begin iterator is not decrementable and the behavior is undefined if --container.begin() is evaluated.
Other containers like std::vector use more expansive notions like LegacyRandomAccessIterator, but there's nothing there that changes the behavior of decrementing a begin iterator.

Why cannot this simple vector c++ program work?

I am learning to use stl vector and It is odd that this program cannot work. What is wrong with it? How should I do if I want to implement the same function with vector?
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> vec;
vector<int>::iterator it;
vector<int>::iterator temp;
it = vec.begin();
vec.insert(it, -1);
it++;
vec.insert(it, 2);
for(temp = vec.begin();temp!=vec.end();temp++)
cout<<*temp<<' ';
return 0;
}
vec.insert(it, -1); invalidates it.
You should rather use it = vec.insert(it, -1); which will keep it valid.
You can see the documentation:
https://en.cppreference.com/w/cpp/container/vector
section called "Iterator invalidation" or look at this great question and answer: Iterator invalidation rules
On executing the code
vector<int> vec;
You created an object named vec, it has no elements and vec.size() will be zero.
So what vec.begin() returns is the same as what vec.end() returns.
By doing vec.insert(it, -1); you are inserting a value out of vec's range.
That is undefined behavior.
No, vec.insert(it, -1) works well, but vec.insert(it, -1) causes the vector to reallocate memory for its first element.
That invalids it.
Try vec.emplace_back(-1) or vec.resize(2) instead. They extend vec's size and capacity.

How to efficiently delete elements from a vector given an another vector

What is the best way to delete elements from a vector given an another vector?
I have come up with the following code:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void remove_elements(vector<int>& vDestination, const vector<int>& vSource)
{
if(!vDestination.empty() && !vSource.empty())
{
for(auto i: vSource) {
vDestination.erase(std::remove(vDestination.begin(), vDestination.end(), i), vDestination.end());
}
}
}
int main()
{
vector<int> v1={1,2,3};
vector<int> v2={4,5,6};
vector<int> v3={1,2,3,4,5,6,7,8,9};
remove_elements(v3,v1);
remove_elements(v3,v2);
for(auto i:v3)
cout << i << endl;
return 0;
}
Here the output will be:
7
8
9
My version is the following, I only apply erase after all elements from the vector vSource have been moved to the end by std::remove and keep track of the pointer to the end of the vector vDestination to not iterate over it for nothing.
void remove_elements(vector<int>& vDestination, const vector<int>& vSource)
{
auto last = std::end(vDestination);
std::for_each(std::begin(vSource), std::end(vSource), [&](const int & val) {
last = std::remove(std::begin(vDestination), last, val);
});
vDestination.erase(last, std::end(vDestination));
}
See on coliru : http://coliru.stacked-crooked.com/a/6e86893babb6759c
Update
Here is a template version, so you don't care about the container type :
template <class ContainerA, class ContainerB>
void remove_elements(ContainerA & vDestination, const ContainerB & vSource)
{
auto last = std::end(vDestination);
std::for_each(std::begin(vSource), std::end(vSource), [&](typename ContainerB::const_reference val) {
last = std::remove(std::begin(vDestination), last, val);
});
vDestination.erase(last, std::end(vDestination));
}
Note
This version works for vectors without any constraints, if your vectors are sorted you can take some shortcuts and avoid iterating over and over the vector to delete each element.
I assume that by best you mean fastest that works. Since it's a question about efficiency, I performed a simple benchmark to compare efficiency of several algorithms. Note that they differ a little, since the problem is a bit underspecified - the questions that arise (and assumptions taken for benchmark) are:
is it guaranteed that vDestination contains all elements from vSource ? (assumption: no)
are duplicates allowed in either vDestination or vSource ? (assumption: yes, in both)
does the order of the elements in the result vector matter? (algorithms for both cases tested)
should every element from vDestination be removed if it is equal to any element from vSource, or only one-for-one? (assumption: yes, in both)
are sizes of vDestination and vSource somehow bounded? Is one of them always bigger or much bigger? (several cases tested)
in the comments it's already explained that vectors don't need to be sorted, but I've included this point, as it's not immediately visible from the question (no sorting assumed in either of vectors)
as you see, there are a few points in which algorithms will differ and consequently, as you can guess, best algorithm will depend on your use case. Compared algorithms include:
original one (proposed in question) - baseline
proposed in #dkg answer
proposed in #Revolver_Ocelot answer + additional sorting (required by the algorithm) and pre-reservation of space for result
vector
proposed in #Jarod42 answer
set-based algorithm (presented below - mostly optimization of #Jarod42 algorithm)
counting algorithm (presended below)
set-based algorithm:
std::unordered_set<int> elems(vSource.begin(), vSource.end());
auto i = destination.begin();
auto target = destination.end();
while(i <= target) {
if(elems.count(*i) > 0)
std::swap(*i, *(--target));
else
i++;
}
destination.erase(target, destination.end());
counting algorithm:
std::unordered_map<int, int> counts;
counts.max_load_factor(0.3);
counts.reserve(destination.size());
for(auto v: destination) {
counts[v]++;
}
for(auto v: source) {
counts[v]--;
}
auto i = destination.begin();
for(auto k: counts) {
if(k.second < 1) continue;
i = std::fill_n(i, k.second, k.first);
}
destination.resize(std::distance(destination.begin(), i));
Benchmarking procedure was executed using Celero library and was the following:
Generate n pseudo-random ints (n in set {10,100,1000,10000, 20000, 200000}) and put them to a vector
Copy a fraction (m) of these ints to second vector (fractions from set {0.01, 0.1, 0.2, 0.4, 0.6, 0.8}, min. 1 element)
Start timer
Execute removal procedure
Stop timer
Only algorithms 3, 5 and 6 were executed on datasets larger than 10 000 elements as the rest of them took to long for me to comfortably measure (feel free to do it yourself).
Long story short: if your vectors contain less than 1000 elements, pick whichever you prefer. If they are longer - rely on size of vSource. If it's less than 50% of vDestination - choose set-based algorithm, if it's more - sort them and pick #Revolver_Ocelot's solution (they tie around 60%, with set-based being over 2x faster for vSource being 1% size of vDestination). Please don't rely on order or provide a vector that is sorted from the beginning - requirement that ordering has to remain same slows the process down dramatically. Benchmark on your use case, your compiler, your flags and your hardware. I've attached link to my benchmarks, in case you wanted to reproduce them.
Complete results (file vector-benchmarks.csv) are available on GitHub together with benchmarking code (file tests/benchmarks/vectorRemoval.cpp) here.
Please keep in mind that these are results that I've obtained on my computer, my compiler etc. - in your case they will differ (especially when it comes to point in which one algorithm is better than another).
I've used GCC 6.1.1 with -O3 on Fedora 24, on top of VirtualBox.
If your vectors are always sorted, you can use set_difference:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
void remove_elements(std::vector<int>& vDestination, const std::vector<int>& vSource)
{
std::vector<int> result;
std::set_difference(vDestination.begin(), vDestination.end(), vSource.begin(), vSource.end(), std::back_inserter(result));
vDestination.swap(result);
}
int main()
{
std::vector<int> v1={1,2,3};
std::vector<int> v2={4,5,6};
std::vector<int> v3={1,2,3,4,5,6,7,8,9};
remove_elements(v3,v1);
remove_elements(v3,v2);
for(auto i:v3)
std::cout << i << '\n';
}
If not for reqirement, that output range should not ovelap with any input range, we could even avoid additional vector. Potentially you can roll your own version of set_difference which is allowed to output in range starting with vDestination.begin(), but it is outside of scope of this answer.
Can be written with STL as:
void remove_elements(vector<int>& vDestination, const vector<int>& vSource)
{
const auto isInSource = [&](int e) {
return std::find(vSource.begin(), vSource.end(), e) != vSource.end();
};
vDestination.erase(
std::remove_if(vDestination.begin(), vDestination.end(), isInSource),
vDestination.end());
}
if vSource is sorted, you may replace std::find by std::binary_search.

Iterator assignment in a condition - vector iterators incompatible

I have a wrapper for std::vector and I've implemented function that replaces a section within a vector with another vector. I've tried to put the assignment of an iterator directly in the if condition and got unexpected results.
I'm using Visual Studio 2013 and with FAIL defined I get Debug Assertion Failed! - vector iterators incompatible. Is it possible that the condition is being evaluated from right to left? I can't get over it.
This is a (poorly implemented) code that reproduces my problem - meant to replace 3rd and 4th elements of vec with 1st and 2nd elements of vec_second:
#include <iostream>
#include <vector>
#include <iterator>
using std::cout;
//#define FAIL
int main()
{
std::vector<int> vec = {1, 2, 3, 4, 5};
std::vector<int> vec_second = {6, 7};
auto it = vec.begin() + 2;
#ifndef FAIL
it = vec.erase(it, it + vec_second.size());
if(it == vec.end())
#else
if((it = vec.erase(it, it + vec_second.size())) == vec.end())
#endif
vec.reserve(vec.size() + vec_second.size());
vec.insert(it, vec_second.begin(), vec_second.end());
for(auto const& x : vec)
cout << x << " ";
}
But runs fine on Coliru's GCC.
if((it = vec.erase(it, it + vec_second.size())) == vec.end())
Since there is no sequence point between them, a compiler is free to call erase and end in either order. If end gets called first, that iterator is immediately invalidated by erase, leading to undefined behavior.
Your #ifndef FAIL code is the safe way to do this.

std: container c++ move to front

I'm looking for a std container like a std::list that can efficiently move an element to the front:
a-b-c-d-e
move "b" to front:
a-c-d-e-b
There is no such function in the std containers. Therefor, I think I must combine a remove and push_front function but has anyone can find a better idea?
Thank in advance.
If you don't have to maintain the order of the other elements,
then the simplest solution is doubtlessly just to swap the
element you want with the first element in the container. This
will be efficient with all containers.
Otherwise, std::list offers a splice operation which could
be used. Something like the following, I think:
void
moveToFront(
std::list<MyType>& list,
std::list<MyType>::iterator element )
{
if ( element != list.begin() ) {
list.splice( list.begin(), list, element, std::next( element ) );
}
}
This should end up with only a couple of pointer operations, and
no copies. On the other hand, std::list can be very slow in
general (because of its poor locality); I'd measure very
carefully against the naïve implementation using std::vector,
to make sure it was a win globally. Eliminating all copies here
may not be a win if iterating to find the element you want to
move to the front is ten time more expensive. (A lot of this
depends on how expensive MyType is to copy, and how large it
is. If sizeof(MyType) is close to the size of a page, or
accessing MyType ends up accessing a lot of indirectly
allocated objects, the locality argument won't hold.)
With an std::vector, rather than the obvious erase/insert
void
moveToFront(
std::vector<MyType>& list,
std::vector<MyType>::iterator element )
{
MyType tmp( *element );
std::copy_backwards( list.begin(), std::prev( element ), element );
*list.begin() = tmp;
}
This will result in less copies than the erase (which copies
all of the following elements) insert (which also copies all
of the following elements—which means all of the elements,
because we are inserting at the beginning) pattern.
On std::vector, you could use std::rotate, which has linear complexity
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
std::vector<int> v = { 0, 1, 2, 3, 4 };
int main()
{
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
// swap ranges [1, 2) and [2, 5)
auto it = std::next(v.begin(), 1); // O(1)
auto rb = std::next(it);
auto re = v.end();
std::rotate(it, rb, re); // O(N)
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
}
On a std::list you could use the member function splice, which (given iterators) has constant complexity
#include <algorithm>
#include <iostream>
#include <iterator>
#include <list>
std::list<int> v = { 0, 1, 2, 3, 4 };
int main()
{
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
auto it = std::next(v.begin(), 1); // O(N)
auto rb = std::next(it);
auto re = v.end();
v.splice(it, v, rb, re); // O(1)
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
}
NOTE: the last element is conventially denoted as back in the STL containers, and the first element as front. For std::vector, getting iterators to a certain element is constant time, and swapping is linear time. For std::list, getting iterators is linear time, but splicing into the same list is constant time. However, the much better memory caching behavior of vector is also important as this benchmark by Stroustrup shows.
UPDATE: Several commenters mentioned simply swapping elements: this only applies if you want to transform a-b-c-d-e into a-e-c-d-b. In that case, use std::iter_swap on any container you like. For the transformation of a-b-c-d-e into a-c-d-e-b, use std::rotate or list::splice.