Commit 1858ad16 authored by Nikita Malyavin's avatar Nikita Malyavin

Architecture + extract function ptr from vtable

This patch fixes the architectural problem that was in the previous one:
* class handler is not anymore abused with extra methods that are needed only
locally during update, and belong to sql layer.

* stack is not abused with extra thread_local variables.

The prototype the function must follow now is:
int (*)(T*, const uchar*, const uchar*);

A pointer to member is converted into a function pointer. For that we need to
extract the correct one from vtable. See rtti.h. vtable and pointer to member
formats are implementation-defined, so RTTI::Method_intrn will have to be
implemented for every abi.

The two const uchar* arguments are still excessive for some cases. This only can
be improved with true JIT code generation.

class Exec_plan (exec_plan.h) handles storing, modifying and executing the plan.
class Update_execution_plan hold all the members to call and all the parameters
needed.

Update_execution_plan replaces some variables in update_single_table. It lies on
stack so the performance will be the same.
parent 1e4ba5ae
/*
Copyright (c) 2024, MariaDB
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of
the License.
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 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-1335 USA
*/
#ifndef EXEC_PLAN_INCLUDED
#define EXEC_PLAN_INCLUDED
#include "my_global.h"
#include "my_dbug.h"
#include "rtti.h"
struct Update_execution_plan;
class handler;
struct Exec_plan
{
union Update_func_ptr
{
RTTI::Method_intrn<Update_execution_plan>::as_func update;
RTTI::Method_intrn<handler>::as_func handler;
};
typedef RTTI::Method_intrn<Update_execution_plan>::as_method Update_ptr_to_member;
typedef RTTI::Method_intrn<handler>::as_method Handler_ptr_to_member;
struct Callable
{
void *that;
Update_func_ptr func;
bool has_value() const { return func.handler != NULL; }
int call(const uchar *old_rec, const uchar *new_rec) const
{ return func.handler((handler*)that, old_rec, new_rec); }
};
static constexpr uint MAX_UPDATE_FUNCS= 20;
Callable call_list[MAX_UPDATE_FUNCS + 1] {}; // Initialize, so that the last cell will be NULL
uint next= 0;
// We don't care will it be built of handler or another class. Let's just convert everything to handler.
using Method_intrn= RTTI::Method_intrn<handler>;
void emplace(handler *h, Handler_ptr_to_member member_func, uint idx)
{
DBUG_ASSERT(idx < MAX_UPDATE_FUNCS);
Update_func_ptr func;
// Convert a pointer to member to a function pointer, and also fetch the correct pointer from vtable if needed.
func.handler= Method_intrn::method_to_func(h, member_func);
call_list[idx].that= h;
call_list[idx].func= func;
}
uint add(handler *h, Handler_ptr_to_member member_func)
{
emplace(h, member_func, next);
return next++;
}
void emplace(Update_execution_plan *up, Update_ptr_to_member member_func, uint idx)
{
union
{
Update_ptr_to_member update;
Handler_ptr_to_member handler;
} member_conv;
union
{
handler *handler;
Update_execution_plan *update;
} obj_conv;
member_conv.update= member_func;
obj_conv.update= up;
emplace(obj_conv.handler, member_conv.handler, idx);
}
uint add(Update_execution_plan *up, Update_ptr_to_member member_func)
{
emplace(up, member_func, next);
return next++;
}
};
#endif // EXEC_PLAN_INCLUDED
/*
Copyright (c) 2024, MariaDB
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of
the License.
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 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-1335 USA
*/
#ifndef RTTI_H_INCLUDED
#define RTTI_H_INCLUDED
#include "my_global.h"
#include "my_dbug.h"
#include <stdint.h>
namespace RTTI
{
template<typename T>
struct Method_intrn
{
typedef int (T::*as_method)(const uchar*, const uchar*);
typedef int (*as_func)(T*, const uchar*, const uchar*);
union Ptr_to_member_representation
{
as_method member;
struct
{
uintptr_t ptr;
uintptr_t call_chain_stack;
} s;
};
static
as_method fetch_from_vtable(T *obj, as_method member)
{
Ptr_to_member_representation u;
u.member= member;
// Can't convert method of class nested into a function!
DBUG_ASSERT(u.s.call_chain_stack == 0);
if ((u.s.ptr & 1) == 1) // Virtual. Fetch the real function from vtable
{
char *vtable= *(char**)obj;
uintptr_t func= *(uintptr_t*)&vtable[u.s.ptr-1];
u.s.ptr= func;
}
return u.member;
}
static as_func method_to_func(T *obj, as_method member)
{
Ptr_to_member_representation u;
u.member= fetch_from_vtable(obj, member);
return (as_func) u.s.ptr;
}
};
};
#endif // RTTI_H_INCLUDED
......@@ -48,6 +48,7 @@
#include "rowid_filter.h"
#include "mysys_err.h"
#include "optimizer_defaults.h"
#include "exec_plan.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
......@@ -7986,36 +7987,40 @@ int handler::ha_write_row(const uchar *buf)
DBUG_RETURN(error);
}
void handler::build_ha_update_execution_plan(Update_func funcs[MAX_UPDATE_FUNCS],
Update_execution_markup *array_spec,
uint offset,
bool with_bulk)
void handler::build_ha_update_execution_plan(
Exec_plan *plan,
uint *mark_trx_pos,
Update_execution_plan *obj,
Exec_plan::Update_ptr_to_member mark_trx_and_inc_stats)
{
uint next= 0;
if (table_share->period.unique_keys)
funcs[next++]= &handler::ha_check_overlaps;
plan->add(this, &handler::ha_check_overlaps);
if (table_share->long_unique_table)
funcs[next++]= &handler::check_duplicate_long_entries_update;
plan->add(this, &handler::check_duplicate_long_entries_update);
array_spec->mark_trx_pos= next + offset;
funcs[next++]= &handler::mark_trx_read_write_and_inc_update_stats;
// mark_trx_read_write + statistics_increment function is passed from outside
*mark_trx_pos= plan->add(obj, mark_trx_and_inc_stats);
bool have_traces= tracker != NULL || m_psi != NULL;
#if defined(HAVE_DTRACE) && !defined(DISABLE_DTRACE)
have_traces= true;
#endif
funcs[next++]= with_bulk ? &handler::bulk_update_row_optimized
: have_traces ? &handler::update_row_traced : get_update_row_func();
plan->add(this, have_traces ? &handler::update_row_traced
: &handler::update_row);
bool have_wsrep= IF_WSREP(WSREP_NNULL(ha_thd()), false);
if (row_logging || table_share->online_alter_binlog || have_wsrep)
funcs[next++]= &handler::log_row_for_update;
plan->add(this, &handler::log_row_for_update);
}
array_spec->ha_row_done_pos= next + offset;
funcs[next++]= NULL;
DBUG_ASSERT(next <= MAX_UPDATE_FUNCS);
int handler::mark_trx_and_inc_update_stats(const uchar*, const uchar*)
{
mark_trx_read_write();
increment_statistics(&SSV::ha_update_count);
return 0;
}
......@@ -8043,13 +8048,21 @@ int handler::update_row_traced(const uchar * old_data, const uchar * new_data)
int handler::ha_update_row(const uchar *old_data, const uchar *new_data)
{
Update_func funcs[MAX_UPDATE_FUNCS];
Update_execution_markup array_spec {};
build_ha_update_execution_plan(funcs, &array_spec, 0, false);
if (!exec_plan_initialized)
{
uint update_trx_pos;
build_ha_update_execution_plan(
&exec_plan, &update_trx_pos,
(Update_execution_plan*)this,
(Exec_plan::Update_ptr_to_member)
&handler::mark_trx_and_inc_update_stats);
exec_plan_initialized= true;
}
int error= 0;
for (Update_func *f= funcs; *f && likely(!error); f++)
error= (table->file->**f)(table->record[1], table->record[0]);
for (Exec_plan::Callable *f= exec_plan.call_list;
f->has_value() && likely(!error); f++)
error= f->call(old_data, new_data);
return error;
}
......
......@@ -38,6 +38,7 @@
#include "vers_string.h"
#include "ha_handler_stats.h"
#include "optimizer_costs.h"
#include "exec_plan.h"
#include "sql_analyze_stmt.h" // for Exec_time_tracker
......@@ -2059,6 +2060,8 @@ class partition_info;
struct st_partition_iter;
struct Update_execution_plan;
enum ha_choice { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES, HA_CHOICE_MAX };
enum enum_stats_auto_recalc { HA_STATS_AUTO_RECALC_DEFAULT= 0,
......@@ -3531,33 +3534,20 @@ class handler :public Sql_alloc
void ha_release_auto_increment();
static constexpr int MAX_UPDATE_FUNCS= 10;
typedef int (handler::*Update_func)(const uchar * old_data, const uchar * new_data);
struct Update_execution_markup
{
uint mark_trx_pos;
uint ha_row_done_pos;
uint compare_skips_to_here;
uint all_done;
};
void build_ha_update_execution_plan(handler::Update_func funcs[10], handler::Update_execution_markup *array_spec,
uint offset, bool with_bulk);
friend struct Update_execution_plan; // needed to access private
Exec_plan exec_plan;
bool exec_plan_initialized= false;
void build_ha_update_execution_plan(
Exec_plan *plan,
uint *mark_trx_pos,
Update_execution_plan *obj,
Exec_plan::Update_ptr_to_member mark_trx_and_inc_stats);
int update_row_traced(const uchar * old_data, const uchar * new_data);
int bulk_update_row_optimized(const uchar * old_data, const uchar * new_data);
int inc_update_stats(const uchar*, const uchar*);
int mark_trx_read_write_and_inc_update_stats(const uchar*, const uchar*);
int vers_insert_history(const uchar *, const uchar *);
int period_make_inserts(const uchar *, const uchar *);
int process_after_update_triggers(const uchar *, const uchar *);
int dec_limit_for_update(const uchar *, const uchar *);
int check_view_conds(const uchar *, const uchar *);
int cut_fields_for_portion_of_time(const uchar *, const uchar *);
int invoke_before_triggers(const uchar *, const uchar *);
int compare_records(const uchar *, const uchar *);
int vers_update_end(const uchar *, const uchar *);
virtual Update_func get_update_row_func() const { return &handler::update_row; }
int mark_trx_and_inc_update_stats(const uchar*, const uchar*);
......
This diff is collapsed.
......@@ -83,7 +83,6 @@ class ha_myisam final : public handler
int index_first(uchar * buf) override;
int index_last(uchar * buf) override;
int index_next_same(uchar *buf, const uchar *key, uint keylen) override;
Update_func get_update_row_func() const override { return (Update_func)&ha_myisam::update_row; }
int ft_init() override
{
if (!ft_handler)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment