234 lines
8.1 KiB
C++
234 lines
8.1 KiB
C++
#ifndef SQL_ITERATORS_TIMING_ITERATOR_H_
|
|
#define SQL_ITERATORS_TIMING_ITERATOR_H_
|
|
|
|
/* Copyright (c) 2019, 2024, Oracle and/or its affiliates.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is designed to work with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have either included with
|
|
the program or referenced in the documentation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <chrono>
|
|
|
|
#include "my_alloc.h"
|
|
#include "sql/iterators/row_iterator.h"
|
|
#include "sql/sql_class.h"
|
|
#include "sql/sql_lex.h"
|
|
|
|
/**
|
|
This class is used in implementing the 'EXPLAIN ANALYZE' command.
|
|
It maintains a set of profiling data.
|
|
*/
|
|
class IteratorProfilerImpl final : public IteratorProfiler {
|
|
public:
|
|
// To avoid a lot of repetitive writing.
|
|
using steady_clock = std::chrono::steady_clock;
|
|
using duration = steady_clock::time_point::duration;
|
|
using TimeStamp = steady_clock::time_point;
|
|
|
|
/** Return current time.*/
|
|
static TimeStamp Now() {
|
|
#if defined(__linux__)
|
|
// Work around very slow libstdc++ implementations of std::chrono
|
|
// (those compiled with _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL).
|
|
timespec tp;
|
|
clock_gettime(CLOCK_MONOTONIC, &tp);
|
|
return steady_clock::time_point(
|
|
steady_clock::duration(std::chrono::seconds(tp.tv_sec) +
|
|
std::chrono::nanoseconds(tp.tv_nsec)));
|
|
#else
|
|
return steady_clock::now();
|
|
#endif
|
|
}
|
|
|
|
double GetFirstRowMs() const override {
|
|
return DurationToMs(m_elapsed_first_row);
|
|
}
|
|
double GetLastRowMs() const override {
|
|
return DurationToMs(m_elapsed_first_row + m_elapsed_other_rows);
|
|
}
|
|
|
|
uint64_t GetNumInitCalls() const override { return m_num_init_calls; }
|
|
uint64_t GetNumRows() const override { return m_num_rows; }
|
|
|
|
/** Mark the end of an iterator->Init() call.*/
|
|
void StopInit(TimeStamp start_time) {
|
|
m_elapsed_first_row += Now() - start_time;
|
|
m_num_init_calls++;
|
|
m_first_row = true;
|
|
}
|
|
|
|
/**
|
|
Update the number of rows read. Note that this function is only called
|
|
for iterator where we read all rows during iterator->Init()
|
|
(@see MaterializeIterator and @see TemptableAggregateIterator).
|
|
*/
|
|
void IncrementNumRows(uint64_t materialized_rows) {
|
|
m_num_rows += materialized_rows;
|
|
}
|
|
|
|
/**
|
|
Mark the end of an iterator->Read() call.
|
|
@param start_time time when Read() started.
|
|
@param read_ok 'true' if Read() was successful.
|
|
*/
|
|
void StopRead(TimeStamp start_time, bool read_ok) {
|
|
if (m_first_row) {
|
|
m_elapsed_first_row += Now() - start_time;
|
|
m_first_row = false;
|
|
} else {
|
|
m_elapsed_other_rows += Now() - start_time;
|
|
}
|
|
if (read_ok) {
|
|
m_num_rows++;
|
|
}
|
|
}
|
|
|
|
private:
|
|
static double DurationToMs(duration dur) {
|
|
return std::chrono::duration<double>(dur).count() * 1e3;
|
|
}
|
|
|
|
/** The number of loops.*/
|
|
uint64_t m_num_init_calls{0};
|
|
|
|
/** The number of rows fetched. (Sum for all loops.)*/
|
|
uint64_t m_num_rows{0};
|
|
|
|
/** True if we are about to read the first row.*/
|
|
bool m_first_row;
|
|
|
|
/**
|
|
Elapsed time in all calls to m_iterator.Init() and Read() for the
|
|
first row.
|
|
*/
|
|
duration m_elapsed_first_row{0};
|
|
|
|
/**
|
|
Elapsed time in all calls to m_iterator.Read() for all but the first
|
|
row.
|
|
*/
|
|
duration m_elapsed_other_rows{0};
|
|
};
|
|
|
|
/**
|
|
An iterator template that wraps a RowIterator, such that all calls to Init()
|
|
and Read() are timed (all others are passed through unchanged, and possibly
|
|
even inlined, since all RowIterator implementations are final). This is used
|
|
for EXPLAIN ANALYZE.
|
|
|
|
Note that MaterializeIterator does not use this class. Doing so
|
|
would give misleading measurements. MaterializeIterator has an
|
|
internal member iterator (m_table_iterator) that iterates over the
|
|
materialized result. Calls to Init()/Read() on that iterator goes
|
|
via Init()/Read() on the MaterializeIterator. And the internal
|
|
iterator is listed above MaterializeIterator in 'EXPLAIN ANALYZE'
|
|
output. Its elapsed time values should thus include both the cost of
|
|
materialization and iterating over the result, while the entry for
|
|
MaterializeIterator should only show the time spent on
|
|
materialization. But if we used TimingIterator, the entry for
|
|
MaterializeIterator would give the sum of time spent on both
|
|
materialization and iteration, and the entry for the internal
|
|
iterator would only show the time spent on iterating over the
|
|
materialized result. (See also Bug #33834146 "'EXPLAIN ANALYZE' cost
|
|
estimates and elapsed time values are not cumulative"). This also
|
|
applies to TemptableAggregateIterator. These classes therefore have
|
|
other mechanisms for obtaining profiling data.
|
|
|
|
See also NewIterator, below.
|
|
*/
|
|
template <class RealIterator>
|
|
class TimingIterator final : public RowIterator {
|
|
public:
|
|
template <class... Args>
|
|
TimingIterator(THD *thd, Args &&...args)
|
|
: RowIterator(thd), m_iterator(thd, std::forward<Args>(args)...) {}
|
|
|
|
bool Init() override {
|
|
const IteratorProfilerImpl::TimeStamp start_time =
|
|
IteratorProfilerImpl::Now();
|
|
bool err = m_iterator.Init();
|
|
m_profiler.StopInit(start_time);
|
|
return err;
|
|
}
|
|
|
|
int Read() override {
|
|
const IteratorProfilerImpl::TimeStamp start_time =
|
|
IteratorProfilerImpl::Now();
|
|
int err = m_iterator.Read();
|
|
m_profiler.StopRead(start_time, err == 0);
|
|
return err;
|
|
}
|
|
|
|
void SetNullRowFlag(bool is_null_row) override {
|
|
m_iterator.SetNullRowFlag(is_null_row);
|
|
}
|
|
void UnlockRow() override { m_iterator.UnlockRow(); }
|
|
void StartPSIBatchMode() override { m_iterator.StartPSIBatchMode(); }
|
|
void EndPSIBatchModeIfStarted() override {
|
|
m_iterator.EndPSIBatchModeIfStarted();
|
|
}
|
|
|
|
void SetOverrideProfiler(const IteratorProfiler *profiler) override {
|
|
m_override_profiler = profiler;
|
|
}
|
|
|
|
const IteratorProfiler *GetProfiler() const override {
|
|
return m_override_profiler == nullptr ? &m_profiler : m_override_profiler;
|
|
}
|
|
|
|
RealIterator *real_iterator() override { return &m_iterator; }
|
|
const RealIterator *real_iterator() const override { return &m_iterator; }
|
|
|
|
private:
|
|
/** This maintains the profiling measurements.*/
|
|
IteratorProfilerImpl m_profiler;
|
|
|
|
/**
|
|
For iterators over materialized tables we must make profiling
|
|
measurements in a different way. This field keeps those measurements.
|
|
*/
|
|
const IteratorProfiler *m_override_profiler{nullptr};
|
|
|
|
RealIterator m_iterator;
|
|
};
|
|
|
|
// Allocates a new iterator on the given MEM_ROOT. The MEM_ROOT must live
|
|
// for at least as long as the iterator does.
|
|
//
|
|
// If we are in EXPLAIN ANALYZE, the iterator is wrapped in a TimingIterator<T>,
|
|
// so that it collects timing information. For this reason, nearly all
|
|
// instantiations of iterators should go through this function.
|
|
|
|
template <class RealIterator, class... Args>
|
|
unique_ptr_destroy_only<RowIterator> NewIterator(THD *thd, MEM_ROOT *mem_root,
|
|
Args &&...args) {
|
|
if (thd->lex->is_explain_analyze) {
|
|
return unique_ptr_destroy_only<RowIterator>(
|
|
new (mem_root)
|
|
TimingIterator<RealIterator>(thd, std::forward<Args>(args)...));
|
|
} else {
|
|
return unique_ptr_destroy_only<RowIterator>(
|
|
new (mem_root) RealIterator(thd, std::forward<Args>(args)...));
|
|
}
|
|
}
|
|
|
|
#endif // SQL_ITERATORS_TIMING_ITERATOR_H_
|