diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b0564b2468..7aed0b4111f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### Internals * The release binaries for Apple platforms are now built with Xcode 9.4 (up from 9.2). +* Performance of queries on Timestamp is improved ---------------------------------------------- diff --git a/src/realm/column_timestamp.cpp b/src/realm/column_timestamp.cpp index bfe11703739..701978834fe 100644 --- a/src/realm/column_timestamp.cpp +++ b/src/realm/column_timestamp.cpp @@ -354,6 +354,18 @@ void TimestampColumn::leaf_to_dot(MemRef, ArrayParent*, size_t /*ndx_in_parent*/ // FIXME: Dummy implementation } +void TimestampColumn::get_seconds_leaf(size_t ndx, size_t& ndx_in_leaf, + BpTree>::LeafInfo& inout_leaf_info) const noexcept +{ + m_seconds->get_leaf(ndx, ndx_in_leaf, inout_leaf_info); +} + +void TimestampColumn::get_nanoseconds_leaf(size_t ndx, size_t& ndx_in_leaf, + BpTree::LeafInfo& inout_leaf) const noexcept +{ + m_nanoseconds->get_leaf(ndx, ndx_in_leaf, inout_leaf); +} + // LCOV_EXCL_STOP ignore debug functions void TimestampColumn::add(const Timestamp& ts) diff --git a/src/realm/column_timestamp.hpp b/src/realm/column_timestamp.hpp index d5c4859d76a..cffaa90de0f 100644 --- a/src/realm/column_timestamp.hpp +++ b/src/realm/column_timestamp.hpp @@ -83,6 +83,9 @@ class TimestampColumn : public ColumnBaseSimple { void to_dot(std::ostream&, StringData title = StringData()) const override; void do_dump_node_structure(std::ostream&, int level) const override; void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const override; + void get_seconds_leaf(size_t ndx, size_t& ndx_in_leaf, + BpTree>::LeafInfo& inout_leaf) const noexcept; + void get_nanoseconds_leaf(size_t ndx, size_t& ndx_in_leaf, BpTree::LeafInfo& inout_leaf) const noexcept; void add(const Timestamp& ts = Timestamp{}); Timestamp get(size_t row_ndx) const noexcept; diff --git a/src/realm/query_engine.cpp b/src/realm/query_engine.cpp index af16336c1dc..0ab90fcc110 100644 --- a/src/realm/query_engine.cpp +++ b/src/realm/query_engine.cpp @@ -684,3 +684,148 @@ ExpressionNode::ExpressionNode(const ExpressionNode& from, QueryNodeHandoverPatc , m_expression(from.m_expression->clone(patches)) { } + +namespace realm { +template <> +size_t TimestampNode::find_first_local(size_t start, size_t end) +{ + REALM_ASSERT(this->m_table); + + if (this->m_value.is_null()) { + return not_found; + } + while (start < end) { + size_t ret = this->find_first_local_seconds(start, end); + + if (ret == not_found) + return not_found; + + util::Optional seconds = get_seconds_and_cache(ret); + if (!seconds) { + start = ret + 1; + continue; + } + if (*seconds > m_value.get_seconds()) { + return ret; + } + // We now know that neither m_value nor current value is null and that seconds part equals + // We are just missing to compare nanoseconds part + int32_t nanos = this->get_nanoseconds_and_cache(ret); + if (nanos > m_value.get_nanoseconds()) { + return ret; + } + start = ret + 1; + } + + return not_found; +} + +template <> +size_t TimestampNode::find_first_local(size_t start, size_t end) +{ + REALM_ASSERT(this->m_table); + + if (this->m_value.is_null()) { + return not_found; + } + while (start < end) { + size_t ret = this->find_first_local_seconds(start, end); + + if (ret == not_found) + return not_found; + + util::Optional seconds = get_seconds_and_cache(ret); + if (!seconds) { + start = ret + 1; + continue; + } + if (*seconds < m_value.get_seconds()) { + return ret; + } + // We now know that neither m_value nor current value is null and that seconds part equals + // We are just missing to compare nanoseconds part + int32_t nanos = this->get_nanoseconds_and_cache(ret); + if (nanos < m_value.get_nanoseconds()) { + return ret; + } + start = ret + 1; + } + + return not_found; +} + +template <> +size_t TimestampNode::find_first_local(size_t start, size_t end) +{ + REALM_ASSERT(this->m_table); + + if (this->m_value.is_null()) { + return not_found; + } + while (start < end) { + size_t ret = this->find_first_local_seconds(start, end); + + if (ret == not_found) + return not_found; + + util::Optional seconds = get_seconds_and_cache(ret); + if (!seconds) { // null equality + start = ret + 1; + continue; + } + if (*seconds > m_value.get_seconds()) { + return ret; + } + // We now know that neither m_value nor current value is null and that seconds part equals + // We are just missing to compare nanoseconds part + int32_t nanos = this->get_nanoseconds_and_cache(ret); + if (nanos >= m_value.get_nanoseconds()) { + return ret; + } + start = ret + 1; + } + + return not_found; +} + +template <> +size_t TimestampNode::find_first_local(size_t start, size_t end) +{ + REALM_ASSERT(this->m_table); + + if (this->m_value.is_null()) { + return not_found; + } + while (start < end) { + size_t ret = this->find_first_local_seconds(start, end); + + if (ret == not_found) + return not_found; + + util::Optional seconds = get_seconds_and_cache(ret); + if (!seconds) { // null equality + start = ret + 1; + continue; + } + if (*seconds < m_value.get_seconds()) { + return ret; + } + // We now know that neither m_value nor current value is null and that seconds part equals + // We are just missing to compare nanoseconds part + int32_t nanos = this->get_nanoseconds_and_cache(ret); + if (nanos <= m_value.get_nanoseconds()) { + return ret; + } + start = ret + 1; + } + + return not_found; +} +#ifdef _WIN32 +// Explicit instantiation required on some windows builds +template size_t TimestampNode::find_first_local(size_t start, size_t end); +template size_t TimestampNode::find_first_local(size_t start, size_t end); +template size_t TimestampNode::find_first_local(size_t start, size_t end); +template size_t TimestampNode::find_first_local(size_t start, size_t end); +#endif +} // namespace realm diff --git a/src/realm/query_engine.hpp b/src/realm/query_engine.hpp index 12d1d161adc..7f87be1bf3c 100644 --- a/src/realm/query_engine.hpp +++ b/src/realm/query_engine.hpp @@ -1283,20 +1283,24 @@ class BinaryNode : public ParentNode { }; -template -class TimestampNode : public ParentNode { +class TimestampNodeBase : public ParentNode { public: using TConditionValue = Timestamp; static const bool special_null_node = false; + using LeafTypeSeconds = typename IntNullColumn::LeafType; + using LeafInfoSeconds = typename IntNullColumn::LeafInfo; + using LeafTypeNanos = typename IntegerColumn::LeafType; + using LeafInfoNanos = typename IntegerColumn::LeafInfo; + - TimestampNode(Timestamp v, size_t column) + TimestampNodeBase(Timestamp v, size_t column) : m_value(v) { m_condition_column_idx = column; } - TimestampNode(null, size_t column) - : TimestampNode(Timestamp{}, column) + TimestampNodeBase(null, size_t column) + : TimestampNodeBase(Timestamp{}, column) { } @@ -1315,27 +1319,54 @@ class TimestampNode : public ParentNode { ParentNode::init(); m_dD = 100.0; + + // Clear leaf cache + m_leaf_end_seconds = 0; + m_array_ptr_seconds.reset(); // Explicitly destroy the old one first, because we're reusing the memory. + m_array_ptr_seconds.reset(new (&m_leaf_cache_storage_seconds) LeafTypeSeconds(m_table->get_alloc())); + m_leaf_end_nanos = 0; + m_array_ptr_nanos.reset(); // Explicitly destroy the old one first, because we're reusing the memory. + m_array_ptr_nanos.reset(new (&m_leaf_cache_storage_nanos) LeafTypeNanos(m_table->get_alloc())); } - size_t find_first_local(size_t start, size_t end) override +protected: + void get_leaf_seconds(const TimestampColumn& col, size_t ndx) { - size_t ret = m_condition_column->find(m_value, start, end); - return ret; + size_t ndx_in_leaf; + LeafInfoSeconds leaf_info_seconds{&m_leaf_ptr_seconds, m_array_ptr_seconds.get()}; + col.get_seconds_leaf(ndx, ndx_in_leaf, leaf_info_seconds); + m_leaf_start_seconds = ndx - ndx_in_leaf; + m_leaf_end_seconds = m_leaf_start_seconds + m_leaf_ptr_seconds->size(); } - virtual std::string describe(util::serializer::SerialisationState& state) const override + void get_leaf_nanos(const TimestampColumn& col, size_t ndx) { - REALM_ASSERT(m_condition_column != nullptr); - return state.describe_column(ParentNode::m_table, m_condition_column->get_column_index()) - + " " + TConditionFunction::description() + " " + util::serializer::print_value(TimestampNode::m_value); + size_t ndx_in_leaf; + LeafInfoNanos leaf_info_nanos{&m_leaf_ptr_nanos, m_array_ptr_nanos.get()}; + col.get_nanoseconds_leaf(ndx, ndx_in_leaf, leaf_info_nanos); + m_leaf_start_nanos = ndx - ndx_in_leaf; + m_leaf_end_nanos = m_leaf_start_nanos + m_leaf_ptr_nanos->size(); } - std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + util::Optional get_seconds_and_cache(size_t ndx) { - return std::unique_ptr(new TimestampNode(*this, patches)); + // Cache internal leaves + if (ndx >= this->m_leaf_end_seconds || ndx < this->m_leaf_start_seconds) { + this->get_leaf_seconds(*this->m_condition_column, ndx); + } + return this->m_leaf_ptr_seconds->get(ndx - this->m_leaf_start_seconds); + } + + int32_t get_nanoseconds_and_cache(size_t ndx) + { + // Cache internal leaves + if (ndx >= this->m_leaf_end_nanos || ndx < this->m_leaf_start_nanos) { + this->get_leaf_nanos(*this->m_condition_column, ndx); + } + return int32_t(this->m_leaf_ptr_nanos->get(ndx - this->m_leaf_start_nanos)); } - TimestampNode(const TimestampNode& from, QueryNodeHandoverPatches* patches) + TimestampNodeBase(const TimestampNodeBase& from, QueryNodeHandoverPatches* patches) : ParentNode(from, patches) , m_value(from.m_value) , m_condition_column(from.m_condition_column) @@ -1344,11 +1375,94 @@ class TimestampNode : public ParentNode { m_condition_column_idx = m_condition_column->get_column_index(); } -private: Timestamp m_value; const TimestampColumn* m_condition_column; + + // Leaf cache seconds + using LeafCacheStorageSeconds = + typename std::aligned_storage::type; + LeafCacheStorageSeconds m_leaf_cache_storage_seconds; + std::unique_ptr m_array_ptr_seconds; + const LeafTypeSeconds* m_leaf_ptr_seconds = nullptr; + size_t m_leaf_start_seconds = npos; + size_t m_leaf_end_seconds = 0; + + // Leaf cache nanoseconds + using LeafCacheStorageNanos = typename std::aligned_storage::type; + LeafCacheStorageNanos m_leaf_cache_storage_nanos; + std::unique_ptr m_array_ptr_nanos; + const LeafTypeNanos* m_leaf_ptr_nanos = nullptr; + size_t m_leaf_start_nanos = npos; + size_t m_leaf_end_nanos = 0; +}; + +template +class TimestampNode : public TimestampNodeBase { +public: + using TimestampNodeBase::TimestampNodeBase; + + template + size_t find_first_local_seconds(size_t start, size_t end) + { + REALM_ASSERT(!this->m_value.is_null()); + while (start < end) { + // Cache internal leaves + if (start >= this->m_leaf_end_seconds || start < this->m_leaf_start_seconds) { + this->get_leaf_seconds(*this->m_condition_column, start); + } + + size_t end2; + if (end > this->m_leaf_end_seconds) + end2 = this->m_leaf_end_seconds - this->m_leaf_start_seconds; + else + end2 = end - this->m_leaf_start_seconds; + + int64_t needle = this->m_value.get_seconds(); + size_t s = this->m_leaf_ptr_seconds->template find_first( + needle, start - this->m_leaf_start_seconds, end2); + + if (s == not_found) { + start = this->m_leaf_end_seconds; + continue; + } + return s + this->m_leaf_start_seconds; + } + return not_found; + } + + // see query_engine.cpp for operator specialisations + size_t find_first_local(size_t start, size_t end) override + { + REALM_ASSERT(this->m_table); + + size_t ret = m_condition_column->find(m_value, start, end); + return ret; + } + + virtual std::string describe(util::serializer::SerialisationState& state) const override + { + REALM_ASSERT(m_condition_column != nullptr); + return state.describe_column(ParentNode::m_table, m_condition_column->get_column_index()) + " " + + TConditionFunction::description() + " " + util::serializer::print_value(TimestampNode::m_value); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new TimestampNode(*this, patches)); + } }; +template <> +size_t TimestampNode::find_first_local(size_t start, size_t end); +template <> +size_t TimestampNode::find_first_local(size_t start, size_t end); +template <> +size_t TimestampNode::find_first_local(size_t start, size_t end); +template <> +size_t TimestampNode::find_first_local(size_t start, size_t end); + + + class StringNodeBase : public ParentNode { public: using TConditionValue = StringData; @@ -2248,7 +2362,6 @@ class TwoColumnsNode : public ParentNode { // For Next-Generation expressions like col1 / col2 + 123 > col4 * 100. class ExpressionNode : public ParentNode { - public: ExpressionNode(std::unique_ptr); diff --git a/test/benchmark-common-tasks/main.cpp b/test/benchmark-common-tasks/main.cpp index 2bfb1916471..601dbed1bf0 100644 --- a/test/benchmark-common-tasks/main.cpp +++ b/test/benchmark-common-tasks/main.cpp @@ -17,6 +17,7 @@ **************************************************************************/ #include +#include #include #include @@ -40,7 +41,7 @@ using namespace realm::util; using namespace realm::test_util; namespace { -#define BASE_SIZE 3600 +#define BASE_SIZE 36000 /** This bechmark suite represents a number of common use cases, @@ -320,6 +321,127 @@ struct BenchmarkWithLongStrings : BenchmarkWithStrings { } }; +struct BenchmarkWithTimestamps : Benchmark { + std::multiset values; + Timestamp needle; + size_t num_results_to_needle; + double percent_results_to_needle = 0.5; + void before_all(SharedGroup& group) + { + WriteTransaction tr(group); + TableRef t = tr.add_table("Timestamps"); + t->add_column(type_Timestamp, "timestamps"); + t->add_empty_row(BASE_SIZE * 10); + Random r; + for (size_t i = 0; i < BASE_SIZE * 10; ++i) { + Timestamp time{r.draw_int(0, 1000000), r.draw_int(0, 1000000)}; + t->set_timestamp(0, i, time); + values.insert(time); + } + tr.commit(); + // simulate a work load where this percent of random results match + num_results_to_needle = size_t(values.size() * percent_results_to_needle); + // this relies on values being stored in sorted order by std::multiset + auto it = values.begin(); + for (size_t i = 0; i < num_results_to_needle; ++i) { + ++it; + } + needle = *it; + } + + void after_all(SharedGroup& group) + { + Group& g = group.begin_write(); + g.remove_table("Timestamps"); + group.commit(); + } +}; + +struct BenchmarkQueryTimestampGreater : BenchmarkWithTimestamps { + void before_all(SharedGroup& group) { + percent_results_to_needle = 2.0f / 3.0f; + BenchmarkWithTimestamps::before_all(group); + } + const char* name() const + { + return "QueryTimestampGreater"; + } + + void operator()(SharedGroup& group) + { + ReadTransaction tr(group); + ConstTableRef table = tr.get_table("Timestamps"); + Query query = table->where().greater(0, needle); + TableView results = query.find_all(); + REALM_ASSERT_EX(results.size() == values.size() - num_results_to_needle - 1, results.size(), num_results_to_needle, values.size()); + static_cast(results); + } +}; + +struct BenchmarkQueryTimestampGreaterEqual : BenchmarkWithTimestamps { + void before_all(SharedGroup& group) { + percent_results_to_needle = 2.0f / 3.0f; + BenchmarkWithTimestamps::before_all(group); + } + const char* name() const + { + return "QueryTimestampGreaterEqual"; + } + + void operator()(SharedGroup& group) + { + ReadTransaction tr(group); + ConstTableRef table = tr.get_table("Timestamps"); + Query query = table->where().greater_equal(0, needle); + TableView results = query.find_all(); + REALM_ASSERT_EX(results.size() == values.size() - num_results_to_needle, results.size(), num_results_to_needle, values.size()); + static_cast(results); + } +}; + + +struct BenchmarkQueryTimestampLess : BenchmarkWithTimestamps { + void before_all(SharedGroup& group) { + percent_results_to_needle = 1.0f / 3.0f; + BenchmarkWithTimestamps::before_all(group); + } + const char* name() const + { + return "QueryTimestampLess"; + } + + void operator()(SharedGroup& group) + { + ReadTransaction tr(group); + ConstTableRef table = tr.get_table("Timestamps"); + Query query = table->where().less(0, needle); + TableView results = query.find_all(); + REALM_ASSERT_EX(results.size() == num_results_to_needle, results.size(), num_results_to_needle, values.size()); + static_cast(results); + } +}; + +struct BenchmarkQueryTimestampLessEqual : BenchmarkWithTimestamps { + void before_all(SharedGroup& group) { + percent_results_to_needle = 1.0f / 3.0f; + BenchmarkWithTimestamps::before_all(group); + } + const char* name() const + { + return "QueryTimestampLessEqual"; + } + + void operator()(SharedGroup& group) + { + ReadTransaction tr(group); + ConstTableRef table = tr.get_table("Timestamps"); + Query query = table->where().less_equal(0, needle); + TableView results = query.find_all(); + REALM_ASSERT_EX(results.size() == num_results_to_needle + 1, results.size(), num_results_to_needle, values.size()); + static_cast(results); + } +}; + struct BenchmarkWithIntsTable : Benchmark { void before_all(SharedGroup& group) { @@ -1144,6 +1266,11 @@ int benchmark_common_tasks_main() BENCH(BenchmarkQueryIntEquality); BENCH(BenchmarkQueryIntEqualityIndexed); + BENCH(BenchmarkQueryTimestampGreater); + BENCH(BenchmarkQueryTimestampGreaterEqual); + BENCH(BenchmarkQueryTimestampLess); + BENCH(BenchmarkQueryTimestampLessEqual); + #undef BENCH return 0; } diff --git a/test/test_parser.cpp b/test/test_parser.cpp index d0fca85b380..3b5702824a5 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -384,7 +384,6 @@ Query verify_query(test_util::unit_test::TestContext& test_context, TableRef t, parser::ParserResult res2 = realm::parser::parse(description); realm::query_builder::apply_predicate(q2, res2.predicate, args); - CHECK_EQUAL(q2.count(), num_results); return q2; } @@ -454,9 +453,11 @@ TEST(Parser_basic_serialisation) TableRef t = g.add_table(table_name); size_t int_col_ndx = t->add_column(type_Int, "age"); size_t str_col_ndx = t->add_column(type_String, "name"); - size_t double_col_ndx = t->add_column(type_Double, "fees"); + size_t double_col_ndx = t->add_column(type_Double, "fees", true); + size_t bool_col_ndx = t->add_column(type_Bool, "licensed", true); size_t link_col_ndx = t->add_column_link(type_Link, "buddy", *t); size_t time_col_ndx = t->add_column(type_Timestamp, "time", true); + t->add_search_index(int_col_ndx); t->add_empty_row(5); std::vector names = {"Billy", "Bob", "Joe", "Jane", "Joel"}; std::vector fees = { 2.0, 2.23, 2.22, 2.25, 3.73 }; @@ -465,6 +466,7 @@ TEST(Parser_basic_serialisation) t->set_int(int_col_ndx, i, i); t->set_string(str_col_ndx, i, names[i]); t->set_double(double_col_ndx, i, fees[i]); + t->set_bool(bool_col_ndx, i, i % 2 == 0); } t->set_timestamp(time_col_ndx, 0, Timestamp(realm::null())); t->set_timestamp(time_col_ndx, 1, Timestamp(1512130073, 0)); // 2017/12/02 @ 12:47am (UTC) @@ -493,8 +495,24 @@ TEST(Parser_basic_serialisation) verify_query(test_context, t, "3 =< age", 2); verify_query(test_context, t, "age > 2 and age < 4", 1); verify_query(test_context, t, "age = 1 || age == 3", 2); + verify_query(test_context, t, "fees = 1.2 || fees = 2.23", 1); + verify_query(test_context, t, "fees = 2 || fees = 3", 1); + verify_query(test_context, t, "fees = 2 || fees = 3 || fees = 4", 1); + verify_query(test_context, t, "fees = 0 || fees = 1", 0); + verify_query(test_context, t, "fees != 2.22 && fees > 2.2", 3); verify_query(test_context, t, "(age > 1 || fees >= 2.25) && age == 4", 1); + verify_query(test_context, t, "licensed == true", 3); + verify_query(test_context, t, "licensed == false", 2); + verify_query(test_context, t, "licensed = true || licensed = true", 3); + verify_query(test_context, t, "licensed = 1 || licensed = 0", 5); + verify_query(test_context, t, "licensed = true || licensed = false", 5); + verify_query(test_context, t, "licensed == true || licensed == false", 5); + verify_query(test_context, t, "licensed == true || buddy.licensed == true", 3); + verify_query(test_context, t, "buddy.licensed == true", 0); + verify_query(test_context, t, "buddy.licensed == false", 1); + verify_query(test_context, t, "licensed == false || buddy.licensed == false", 3); + verify_query(test_context, t, "licensed == true or licensed = true || licensed = TRUE", 3); verify_query(test_context, t, "name = \"Joe\"", 1); verify_query(test_context, t, "buddy.age > 0", 1); verify_query(test_context, t, "name BEGINSWITH \"J\"", 3); @@ -521,6 +539,7 @@ TEST(Parser_basic_serialisation) CHECK(message.find("missing_property") != std::string::npos); } + TEST(Parser_LinksToSameTable) { Group g; diff --git a/test/test_query.cpp b/test/test_query.cpp index 45092e8b9a8..ca005f2c28f 100644 --- a/test/test_query.cpp +++ b/test/test_query.cpp @@ -10609,6 +10609,18 @@ TEST(Query_Timestamp) match = (first != null{}).count(); CHECK_EQUAL(match, 5); + match = (first > null{}).count(); + CHECK_EQUAL(match, 0); + + match = (first < null{}).count(); + CHECK_EQUAL(match, 0); + + match = (first >= null{}).count(); + CHECK_EQUAL(match, 1); + + match = (first <= null{}).count(); + CHECK_EQUAL(match, 1); + match = (first != Timestamp(0, 0)).count(); CHECK_EQUAL(match, 5); @@ -10673,6 +10685,28 @@ TEST(Query_Timestamp) CHECK_EQUAL(match, npos); // Note that (null < null) == false } +TEST(Query_TimestampCount) +{ + Table table; + auto col_date = table.add_column(type_Timestamp, "date", true); + for (int i = 0; i < 10; i++) { + auto ndx = table.add_empty_row(); + table.set_timestamp(col_date, ndx, Timestamp(i / 4, i % 4)); + } + table.set_null(col_date, 5); + + // Timestamps : {0,0}, {0,1}, {0,2}, {0,3}, {1,0}, {}, {1,2}, {1,3}, {2,0}, {2,1} + + auto timestamps = table.column(col_date); + + CHECK_EQUAL((timestamps > Timestamp(0, 3)).count(), 5); + CHECK_EQUAL((timestamps >= Timestamp(0, 3)).count(), 6); + CHECK_EQUAL((timestamps < Timestamp(1, 3)).count(), 6); + CHECK_EQUAL((timestamps <= Timestamp(1, 3)).count(), 7); + CHECK_EQUAL((timestamps == Timestamp()).count(), 1); + CHECK_EQUAL((timestamps != Timestamp()).count(), 9); +} + TEST(Query_Timestamp_Null) { // Test that querying for null on non-nullable column (with default value being non-null value) is diff --git a/test/test_shared.cpp b/test/test_shared.cpp index e8211fff1dd..9fe630f2450 100644 --- a/test/test_shared.cpp +++ b/test/test_shared.cpp @@ -4008,4 +4008,32 @@ TEST(Shared_GetCommitSize) } } +/* +#include +TEST(Shared_TimestampQuery) +{ + Table table; + auto col_date = table.add_column(type_Timestamp, "date", true); + + Random random(random_int()); // Seed from slow global generator + + for (int i = 0; i < 10000; i++) { + auto ndx = table.add_empty_row(); + int seconds = random.draw_int_max(3600 * 24 * 10); + table.set_timestamp(col_date, ndx, Timestamp(seconds, 0)); + } + + Query q = table.column(col_date) > Timestamp(3600 * 24 * 5, 3); + auto start = std::chrono::steady_clock::now(); + CALLGRIND_START_INSTRUMENTATION; + auto cnt = q.count(); + CALLGRIND_STOP_INSTRUMENTATION; + auto end = std::chrono::steady_clock::now(); + + std::cout << "Time: " << std::chrono::duration_cast(end - start).count() << " us" + << std::endl; + CHECK_GREATER(cnt, 50000); +} +*/ + #endif // TEST_SHARED