I want to copy a vector while setting an elements property to zero. I have a std::vector<PLY>
vector that holds a specific number of the following struct elements:
struct PLY{
float x;
float y;
float z;
}
What is the fastest way to create a copy of this vector where each PLY element has a z-value
of 0
? Is there a faster way then creating a copy of the vector and then iterating over each element to set the new z-value?
what is the fastest way to... ?
Test it. Memory architectures do surprising things.
#include <iostream>
#include <chrono>
#include <vector>
#include <iomanip>
#include <algorithm>
struct PLY
{
PLY() : x(0), y(0), z(0) {}
PLY(float x, float y, float z) : x(x), y(y), z(z) {}
float x, y , z;
};
template<class F>
std::vector<PLY> test(const char* name, std::vector<PLY> samples, F f)
{
using namespace std::literals;
std::vector<PLY> result;
result.reserve(samples.size());
auto start = std::chrono::high_resolution_clock::now();
f(result, samples);
auto end = std::chrono::high_resolution_clock::now();
using fns = std::chrono::duration<long double, std::chrono::nanoseconds::period>;
using fms = std::chrono::duration<long double, std::chrono::milliseconds::period>;
using fs = std::chrono::duration<long double, std::chrono::seconds::period>;
auto interval = fns(end - start);
auto time_per_sample = interval / samples.size();
auto samples_per_second = 1s / time_per_sample;
std::cout << "testing " << name << '\n';
std::cout << " sample size : " << samples.size() << '\n';
std::cout << " time taken : " << std::fixed << fms(interval).count() << "ms\n";
std::cout << " time per sample : " << std::fixed << (interval / samples.size()).count() << "ns\n";
std::cout << " samples per second : " << std::fixed << samples_per_second << "\n";
return result;
}
struct zero_z_iterator : std::vector<PLY>::const_iterator
{
using base_class = std::vector<PLY>::const_iterator;
using value_type = PLY;
using base_class::base_class;
value_type operator*() const {
auto const& src = base_class::operator*();
return PLY{ src.x, src.y, 0.0 };
}
};
int main()
{
test("transform", std::vector<PLY>(1000000), [](auto& target, auto& source)
{
std::transform(source.begin(), source.end(),
std::back_inserter(target),
[](auto& ply) {
return PLY { ply.x, ply.y, ply.z };
});
});
test("copy and reset z", std::vector<PLY>(1000000), [](auto& target, auto& source)
{
std::copy(source.begin(), source.end(),
std::back_inserter(target));
for (auto& x : target)
{
x.z = 0;
}
});
test("hand_roll", std::vector<PLY>(1000000), [](auto& target, auto& source)
{
for(auto& x : source) {
target.emplace_back(x.x, x.y, 0.0);
}
});
test("assign through custom iterator", std::vector<PLY>(1000000), [](auto& target, auto& source)
{
target.assign(zero_z_iterator(source.begin()),
zero_z_iterator(source.end()));
});
test("transform", std::vector<PLY>(100000000), [](auto& target, auto& source)
{
std::transform(source.begin(), source.end(),
std::back_inserter(target),
[](auto& ply) {
return PLY { ply.x, ply.y, ply.z };
});
});
test("copy and reset z", std::vector<PLY>(100000000), [](auto& target, auto& source)
{
std::copy(source.begin(), source.end(),
std::back_inserter(target));
for (auto& x : target)
{
x.z = 0;
}
});
test("hand_roll", std::vector<PLY>(100000000), [](auto& target, auto& source)
{
for(auto& x : source) {
target.emplace_back(x.x, x.y, 0.0);
}
});
test("assign through custom iterator", std::vector<PLY>(100000000), [](auto& target, auto& source)
{
target.assign(zero_z_iterator(source.begin()),
zero_z_iterator(source.end()));
});
}
testing transform
sample size : 1000000
time taken : 7.495685ms
time per sample : 7.495685ns
samples per second : 133410088.604310
testing copy and reset z
sample size : 1000000
time taken : 3.436614ms
time per sample : 3.436614ns
samples per second : 290984090.735823
testing hand_roll
sample size : 1000000
time taken : 3.289287ms
time per sample : 3.289287ns
samples per second : 304017253.587176
testing assign through custom iterator
sample size : 1000000
time taken : 2.563334ms
time per sample : 2.563334ns
samples per second : 390116933.649692
testing transform
sample size : 100000000
time taken : 768.941767ms
time per sample : 7.689418ns
samples per second : 130048859.733744
testing copy and reset z
sample size : 100000000
time taken : 880.893920ms
time per sample : 8.808939ns
samples per second : 113521046.892911
testing hand_roll
sample size : 100000000
time taken : 769.276240ms
time per sample : 7.692762ns
samples per second : 129992315.894223
testing assign through custom iterator
sample size : 100000000
time taken : 689.493098ms
time per sample : 6.894931ns
samples per second : 145034084.155546
Assign through a custom transform iterator.
template<class Container, class Iter, class TransformFunction>
void assign_transform(Container& target, Iter first, Iter last, TransformFunction func)
{
struct transform_iterator : Iter
{
using base_class = Iter;
using value_type = typename Iter::value_type;
transform_iterator(Iter base, TransformFunction& f)
: base_class(base), func(std::addressof(f))
{}
value_type operator*() const {
auto const& src = base_class::operator*();
return (*func)(src);
}
TransformFunction* func;
};
target.assign(transform_iterator(first, func),
transform_iterator(last, func));
}
use like so:
assign_transform(target, source.begin(), source.end(),
[](auto& from)
{
return PLY(from.x, from.y, 0.0);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With