diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cc27d2d08..97fdb08d83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ If possible, provide tooling that performs the changes, e.g. a shell-script. * `seqan3::cigar` can now be assigned from `std::string_view` ([\#2966](https://github.com/seqan/seqan3/pull/2966)). * Added `seqan3::views::char_strictly_to`. Behaves like `seqan3::views::char_to`, but throws on invalid input ([\#2898](https://github.com/seqan/seqan3/pull/2898)). + * Improved performance of vector assignment for alphabets ([\#3038](https://github.com/seqan/seqan3/pull/3038)). #### I/O * Added `seqan3::sequence_file_option::fasta_ignore_blanks_before_id` to ignore blanks before IDs when reading FASTA diff --git a/include/seqan3/alphabet/alphabet_base.hpp b/include/seqan3/alphabet/alphabet_base.hpp index 53e8146ef3..93ecf5bba0 100644 --- a/include/seqan3/alphabet/alphabet_base.hpp +++ b/include/seqan3/alphabet/alphabet_base.hpp @@ -258,7 +258,7 @@ class alphabet_base private: //!\brief The value of the alphabet letter is stored as the rank. - rank_type rank{}; + rank_type rank; }; } // namespace seqan3 diff --git a/include/seqan3/alphabet/aminoacid/aminoacid_base.hpp b/include/seqan3/alphabet/aminoacid/aminoacid_base.hpp index 58d7b21ed8..6a42edb849 100644 --- a/include/seqan3/alphabet/aminoacid/aminoacid_base.hpp +++ b/include/seqan3/alphabet/aminoacid/aminoacid_base.hpp @@ -28,7 +28,7 @@ namespace seqan3 * \stableapi{Since version 3.1.} */ template -class aminoacid_base : public alphabet_base, public aminoacid_empty_base +class aminoacid_base : public aminoacid_empty_base, public alphabet_base { private: //!\brief Type of the base class. diff --git a/test/performance/range/CMakeLists.txt b/test/performance/range/CMakeLists.txt index fc9860ec6d..bf8f02f620 100644 --- a/test/performance/range/CMakeLists.txt +++ b/test/performance/range/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectories () +seqan3_benchmark (container_assignment_benchmark.cpp) seqan3_benchmark (container_push_back_benchmark.cpp) seqan3_benchmark (container_seq_read_benchmark.cpp) seqan3_benchmark (container_seq_write_benchmark.cpp) diff --git a/test/performance/range/container_assignment_benchmark.cpp b/test/performance/range/container_assignment_benchmark.cpp new file mode 100644 index 0000000000..d62774856f --- /dev/null +++ b/test/performance/range/container_assignment_benchmark.cpp @@ -0,0 +1,152 @@ +// ----------------------------------------------------------------------------------------------------- +// Copyright (c) 2006-2022, Knut Reinert & Freie Universität Berlin +// Copyright (c) 2016-2022, Knut Reinert & MPI für molekulare Genetik +// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License +// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md +// ----------------------------------------------------------------------------------------------------- + +#include + +#include +#include +#include +#include +#include + +using namespace seqan3::test::literals; + +#ifndef NDEBUG +static constexpr size_t vector_size{1_MiB}; +#else +static constexpr size_t vector_size{16_MiB}; +#endif // NDEBUG + +enum tag +{ + assignment_operator, + std_copy, + uninitialized_copy +}; + +template +struct assignment_functor +{ + template + requires (id == tag::assignment_operator) + static constexpr void call(container_t & to, container_t const & from) noexcept( + noexcept(std::is_nothrow_assignable_v)) + { + benchmark::DoNotOptimize(to = from); + } + + template + requires (id == tag::std_copy) + static constexpr void call(container_t & to, container_t const & from) noexcept + { + benchmark::DoNotOptimize(std::copy(std::ranges::begin(from), std::ranges::end(from), std::ranges::begin(to))); + } + + template + requires (id == tag::uninitialized_copy) + static constexpr void call(container_t & to, container_t const & from) noexcept + { + benchmark::DoNotOptimize( + std::uninitialized_copy(std::ranges::begin(from), std::ranges::end(from), std::ranges::begin(to))); + } +}; + +template + requires requires (container_t v) { v.clear(); } +static constexpr void clear(container_t & container) noexcept(noexcept(std::declval().clear())) +{ + container.clear(); +} + +template + requires requires (container_t v) { v.resize(1u); } +static constexpr void resize(container_t & container, + size_t const size) noexcept(noexcept(std::declval().resize(1u))) +{ + container.resize(size); +} + +#if SEQAN3_HAS_SEQAN2 +template +static constexpr void clear(container_t & container) noexcept(noexcept(seqan::clear(std::declval()))) +{ + seqan::clear(container); +} + +template +static constexpr void resize(container_t & container, + size_t const size) noexcept(noexcept(seqan::resize(std::declval(), 1u))) +{ + seqan::resize(container, size); +} +#endif // SEQAN3_HAS_SEQAN2 + +template typename container_t, typename alphabet_t> +static void assign(benchmark::State & state) +{ + auto random_sequence = []() constexpr + { + if constexpr (seqan3::alphabet) + return seqan3::test::generate_sequence(vector_size); +#if SEQAN3_HAS_SEQAN2 + else + return seqan3::test::generate_sequence_seqan2(vector_size); +#endif // SEQAN3_HAS_SEQAN2 + }(); + + container_t from{}; + resize(from, vector_size); + std::copy(std::ranges::begin(random_sequence), std::ranges::end(random_sequence), std::ranges::begin(from)); + container_t to{}; + resize(to, vector_size); + assignment_functor fn{}; + + for (auto _ : state) + { + fn.call(to, from); + benchmark::ClobberMemory(); + clear(to); + benchmark::ClobberMemory(); + } +} + +BENCHMARK_TEMPLATE(assign, tag::assignment_operator, std::vector, seqan3::dna4); +BENCHMARK_TEMPLATE(assign, tag::assignment_operator, std::vector, seqan3::dna5); +BENCHMARK_TEMPLATE(assign, tag::assignment_operator, std::vector, seqan3::aa27); + +BENCHMARK_TEMPLATE(assign, tag::std_copy, std::vector, seqan3::dna4); +BENCHMARK_TEMPLATE(assign, tag::std_copy, std::vector, seqan3::dna5); +BENCHMARK_TEMPLATE(assign, tag::std_copy, std::vector, seqan3::aa27); + +BENCHMARK_TEMPLATE(assign, tag::uninitialized_copy, std::vector, seqan3::dna4); +BENCHMARK_TEMPLATE(assign, tag::uninitialized_copy, std::vector, seqan3::dna5); +BENCHMARK_TEMPLATE(assign, tag::uninitialized_copy, std::vector, seqan3::aa27); + +#if SEQAN3_HAS_SEQAN2 +BENCHMARK_TEMPLATE(assign, tag::assignment_operator, std::vector, seqan::Dna); +BENCHMARK_TEMPLATE(assign, tag::assignment_operator, std::vector, seqan::Dna5); +BENCHMARK_TEMPLATE(assign, tag::assignment_operator, std::vector, seqan::AminoAcid); +BENCHMARK_TEMPLATE(assign, tag::assignment_operator, seqan::String, seqan::Dna); +BENCHMARK_TEMPLATE(assign, tag::assignment_operator, seqan::String, seqan::Dna5); +BENCHMARK_TEMPLATE(assign, tag::assignment_operator, seqan::String, seqan::AminoAcid); + +BENCHMARK_TEMPLATE(assign, tag::std_copy, std::vector, seqan::Dna); +BENCHMARK_TEMPLATE(assign, tag::std_copy, std::vector, seqan::Dna5); +BENCHMARK_TEMPLATE(assign, tag::std_copy, std::vector, seqan::AminoAcid); +BENCHMARK_TEMPLATE(assign, tag::std_copy, seqan::String, seqan::Dna); +BENCHMARK_TEMPLATE(assign, tag::std_copy, seqan::String, seqan::Dna5); +BENCHMARK_TEMPLATE(assign, tag::std_copy, seqan::String, seqan::AminoAcid); + +BENCHMARK_TEMPLATE(assign, tag::uninitialized_copy, std::vector, seqan::Dna); +BENCHMARK_TEMPLATE(assign, tag::uninitialized_copy, std::vector, seqan::Dna5); +BENCHMARK_TEMPLATE(assign, tag::uninitialized_copy, std::vector, seqan::AminoAcid); +BENCHMARK_TEMPLATE(assign, tag::uninitialized_copy, seqan::String, seqan::Dna); +BENCHMARK_TEMPLATE(assign, tag::uninitialized_copy, seqan::String, seqan::Dna5); +BENCHMARK_TEMPLATE(assign, tag::uninitialized_copy, seqan::String, seqan::AminoAcid); +#endif // SEQAN3_HAS_SEQAN2 + +BENCHMARK_MAIN(); diff --git a/test/unit/alphabet/aminoacid/aminoacid_test_template.hpp b/test/unit/alphabet/aminoacid/aminoacid_test_template.hpp index 81a419ced9..6862c345b8 100644 --- a/test/unit/alphabet/aminoacid/aminoacid_test_template.hpp +++ b/test/unit/alphabet/aminoacid/aminoacid_test_template.hpp @@ -20,6 +20,8 @@ TYPED_TEST_SUITE_P(aminoacid); TYPED_TEST_P(aminoacid, concept_check) { + EXPECT_TRUE(std::is_trivial_v); + EXPECT_TRUE(seqan3::aminoacid_alphabet); EXPECT_TRUE(seqan3::aminoacid_alphabet); EXPECT_TRUE(seqan3::aminoacid_alphabet); diff --git a/test/unit/alphabet/nucleotide/nucleotide_test_template.hpp b/test/unit/alphabet/nucleotide/nucleotide_test_template.hpp index 10500a6a9a..d5867cb35b 100644 --- a/test/unit/alphabet/nucleotide/nucleotide_test_template.hpp +++ b/test/unit/alphabet/nucleotide/nucleotide_test_template.hpp @@ -19,6 +19,8 @@ TYPED_TEST_SUITE_P(nucleotide); TYPED_TEST_P(nucleotide, concept_check) { + EXPECT_TRUE(std::is_trivial_v); + EXPECT_TRUE(seqan3::nucleotide_alphabet); EXPECT_TRUE(seqan3::nucleotide_alphabet); EXPECT_TRUE(seqan3::nucleotide_alphabet); diff --git a/test/unit/alphabet/quality/phred_test_template.hpp b/test/unit/alphabet/quality/phred_test_template.hpp index 210f88bc19..c5611e8185 100644 --- a/test/unit/alphabet/quality/phred_test_template.hpp +++ b/test/unit/alphabet/quality/phred_test_template.hpp @@ -17,6 +17,8 @@ TYPED_TEST_SUITE_P(phred); // test provision of data type `phred_type` and phred converter. TYPED_TEST_P(phred, concept_check) { + EXPECT_TRUE(std::is_trivial_v); + EXPECT_TRUE(seqan3::quality_alphabet); EXPECT_TRUE(seqan3::quality_alphabet); EXPECT_TRUE(seqan3::quality_alphabet);