Files
wehub-resource-sync 26446540fa
Lint / lint (push) Has been cancelled
CI / MacOS (push) Has been cancelled
CI / Windows (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

553 lines
23 KiB
C++

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef TVM_S_TIR_META_SCHEDULE_DATABASE_H_
#define TVM_S_TIR_META_SCHEDULE_DATABASE_H_
#include <tvm/ffi/container/array.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ffi/string.h>
#include <tvm/ir/expr.h>
#include <tvm/ir/module.h>
#include <tvm/s_tir/meta_schedule/arg_info.h>
#include <tvm/s_tir/schedule/schedule.h>
#include <tvm/s_tir/schedule/trace.h>
#include <tvm/target/target.h>
#include <filesystem>
#include <memory>
namespace tvm {
namespace s_tir {
namespace meta_schedule {
class ModuleEquality;
/*! \brief A workload, i.e. an IRModule and its structural hash. */
class WorkloadNode : public ffi::Object {
public:
/*! \brief The type of structural hash */
using THashCode = size_t;
/*! \brief The workload's IRModule. */
IRModule mod;
/*! \brief The workload's structural hash. */
THashCode shash;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<WorkloadNode>().def_ro("mod", &WorkloadNode::mod);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("s_tir.meta_schedule.Workload", WorkloadNode, ffi::Object);
/*!
* \brief Export the workload to a JSON string.
* \return An array containing the structural hash and the base64 json string.
*/
ffi::ObjectRef AsJSON() const;
};
/*!
* \brief Managed reference to WorkloadNode.
* \sa WorkloadNode
*/
class Workload : public ffi::ObjectRef {
public:
using THashCode = WorkloadNode::THashCode;
explicit Workload(ffi::ObjectPtr<WorkloadNode> data) : ffi::ObjectRef(data) {}
/*!
* \brief Constructor of Workload.
* \param mod The workload's IRModule.
*/
TVM_DLL explicit Workload(IRModule mod);
/*!
* \brief Constructor of Workload.
* \param mod The workload's IRModule.
* \param shash The workload's structural hash.
*/
TVM_DLL explicit Workload(IRModule mod, THashCode shash);
/*!
* \brief Create a workload from a json object.
* \param json_obj The json object.
* \return The created workload.
*/
TVM_DLL static Workload FromJSON(const ffi::ObjectRef& json_obj);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Workload, ffi::ObjectRef, WorkloadNode);
};
/*! \brief The hash method for Workload */
struct WorkloadHash {
size_t operator()(const Workload& a) const { return a->shash; }
};
/*! \brief The equality check for Workload */
struct WorkloadEqual {
explicit WorkloadEqual(const ModuleEquality& mod_eq) : mod_eq_(mod_eq) {}
bool operator()(const Workload& a, const Workload& b) const;
private:
/*! \brief The module equality testing and hashing method */
const ModuleEquality& mod_eq_;
};
/*! \brief The class of measure candidates. */
class MeasureCandidate;
/*! \brief The class of tuning records. */
class TuningRecordNode : public ffi::Object {
public:
/*! \brief The trace tuned. */
s_tir::Trace trace;
/*! \brief The workload. */
Workload workload{ffi::UnsafeInit()};
/*! \brief The profiling result in seconds. */
ffi::Optional<ffi::Array<FloatImm>> run_secs;
/*! \brief The target for tuning. */
ffi::Optional<Target> target;
/*! \brief The argument information. */
ffi::Optional<ffi::Array<ArgInfo>> args_info;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<TuningRecordNode>()
.def_ro("trace", &TuningRecordNode::trace)
.def_ro("workload", &TuningRecordNode::workload)
.def_ro("run_secs", &TuningRecordNode::run_secs)
.def_ro("target", &TuningRecordNode::target)
.def_ro("args_info", &TuningRecordNode::args_info);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("s_tir.meta_schedule.TuningRecord", TuningRecordNode,
ffi::Object);
/*! \brief Construct the measure candidate given the initial IR module and trace
* stored in the tuning record. */
MeasureCandidate AsMeasureCandidate() const;
/*!
* \brief Export the tuning record to a JSON string.
* \return An array containing the trace, running secs, serialized target, and
* argument information.
*/
ffi::ObjectRef AsJSON() const;
/*!
* \brief Check if this tuning record has valid trace instructions and successful run results.
* \return The check result.
*/
bool IsValid() const;
};
/*!
* \brief The managed reference of TuningRecordNode.
* \sa TuningRecordNode
*/
class TuningRecord : public ffi::ObjectRef {
public:
/*!
\brief Constructor of a tuning record.
\param trace The trace of the tuning record.
\param workload The workload of the tuning record.
\param run_secs The running time of the tuning record.
\param target The target of the tuning record.
\param args_info The argument information of the tuning record.
*/
TVM_DLL explicit TuningRecord(s_tir::Trace trace, Workload workload,
ffi::Optional<ffi::Array<FloatImm>> run_secs,
ffi::Optional<Target> target,
ffi::Optional<ffi::Array<ArgInfo>> args_info);
/*!
* \brief Create a tuning record from a json object.
* \param json_obj The json object.
* \param workload The workload.
* \return The tuning record created.
*/
TVM_DLL static TuningRecord FromJSON(const ffi::ObjectRef& json_obj, const Workload& workload);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(TuningRecord, ffi::ObjectRef, TuningRecordNode);
};
class Database;
/* \brief The abstract interface of database. */
class DatabaseNode : public ffi::Object {
public:
/*!
* \brief Constructor
* \param mod_eq_name A string to specify the module equality testing and hashing method.
* It must be one of the followings:
* - "structural": Use StructuralEqual/Hash
* - "ignore-tensor": Same as "structural", but ignore tensor raw data during
* equality testing and hashing.
* - "anchor-block": Apply equality testing and hashing on the anchor block extracted from a
* given module. The "ignore-tensor" varint is used for the extracted blocks
* or in case no anchor block is found.
* For the definition of the anchor block, see tvm/tirx/analysis.h.
*/
explicit DatabaseNode(ffi::String mod_eq_name = "structural");
/*! \brief Default destructor */
virtual ~DatabaseNode();
/*!
* \brief Check if the database has the given workload.
* \param mod The IRModule to be searched for.
* \return Whether the database has the given workload.
*/
virtual bool HasWorkload(const IRModule& mod) = 0;
/*!
* \brief Look up or add workload to the database if missing.
* \param mod The IRModule to be searched for or added.
* \return The workload corresponding to the given IRModule.
*/
virtual Workload CommitWorkload(const IRModule& mod) = 0;
/*!
* \brief Add a tuning record to the database.
* \param record The tuning record to be added.
*/
virtual void CommitTuningRecord(const TuningRecord& record) = 0;
/*!
* \brief Get the top K valid tuning records of given workload from the database.
* \param workload The workload to be searched for.
* \param top_k The number of top records to be returned.
* \return An array of top K tuning records for the given workload.
*/
virtual ffi::Array<TuningRecord> GetTopK(const Workload& workload, int top_k) = 0;
/*!
* \brief Get all tuning records from the database.
* \return An Array of all the tuning records in the database.
*/
virtual ffi::Array<TuningRecord> GetAllTuningRecords() = 0;
/*!
* \brief Get the size of the database.
* \return The size of the database.
*/
virtual int64_t Size() = 0;
/*!
* \brief Query the best record of the given workload from the database.
* \param mod The IRModule to be searched for.
* \param target The target to be searched for.
* \param workload_name The name of the workload to be searched for.
* \return The best record of the given workload; std::nullopt if not found.
*/
virtual ffi::Optional<TuningRecord> QueryTuningRecord(const IRModule& mod, const Target& target,
const ffi::String& workload_name);
/*!
* \brief Query the best schedule of the given workload from the database.
* \param mod The IRModule to be searched for.
* \param target The target to be searched for.
* \param workload_name The name of the workload to be searched for.
* \return The schedule in the best schedule of the given workload; std::nullopt if not found.
*/
virtual ffi::Optional<s_tir::Schedule> QuerySchedule(const IRModule& mod, const Target& target,
const ffi::String& workload_name);
/*!
* \brief Query the best IRModule of the given workload from the database.
* \param mod The IRModule to be searched for.
* \param target The target to be searched for.
* \param workload_name The name of the workload to be searched for.
* \return The IRModule in the best IRModule of the given workload; std::nullopt if not found.
*/
virtual ffi::Optional<IRModule> QueryIRModule(const IRModule& mod, const Target& target,
const ffi::String& workload_name);
/*!
* \brief Prune the database and dump it a given database.
* \param destination The destination database to be dumped to.
*/
void DumpPruned(Database destination);
/*! \brief Return a reference to the owned module equality method instance. */
const ModuleEquality& GetModuleEquality() const {
TVM_FFI_ICHECK(mod_eq_);
return *mod_eq_;
}
static constexpr const bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO("s_tir.meta_schedule.Database", DatabaseNode, ffi::Object);
private:
/*! \brief The module equality testing and hashing method */
std::unique_ptr<ModuleEquality> mod_eq_;
};
/*! \brief The database with customized methods on the python-side. */
class PyDatabaseNode : public DatabaseNode {
public:
/*!
* \brief Constructor
* \param mod_eq_name A string to specify the module equality testing and hashing method.
* It must be one of the followings:
* - "structural": Use StructuralEqual/Hash
* - "ignore-tensor": Same as "structural", but ignore tensor raw data during
* equality testing and hashing.
* - "anchor-block": Apply equality testing and hashing on the anchor block extracted from a
* given module. The "ignore-tensor" varint is used for the extracted blocks
* or in case no anchor block is found.
* For the definition of the anchor block, see tvm/tirx/analysis.h.
*/
explicit PyDatabaseNode(ffi::String mod_eq_name = "structural");
/*!
* \brief The function type of `HasWorkload` method.
* \param mod The IRModule to be searched for.
* \return Whether the database has the given workload.
*/
using FHasWorkload = ffi::TypedFunction<bool(const IRModule&)>;
/*!
* \brief The function type of `CommitWorkload` method.
* \param mod The IRModule to be searched for or added.
* \return The workload corresponding to the given IRModule.
*/
using FCommitWorkload = ffi::TypedFunction<Workload(const IRModule&)>;
/*!
* \brief The function type of `CommitTuningRecord` method.
* \param record The tuning record to be added.
*/
using FCommitTuningRecord = ffi::TypedFunction<void(const TuningRecord&)>;
/*!
* \brief The function type of `GetTopK` method.
* \param workload The workload to be searched for.
* \param top_k The number of top records to be returned.
* \return An array of top K tuning records for the given workload.
*/
using FGetTopK = ffi::TypedFunction<ffi::Array<TuningRecord>(const Workload&, int)>;
/*!
* \brief The function type of `GetAllTuningRecords` method.
* \return An Array of all the tuning records in the database.
*/
using FGetAllTuningRecords = ffi::TypedFunction<ffi::Array<TuningRecord>()>;
/*!
* \brief The function type of `QueryTuningRecord` method.
* \param mod The IRModule to be searched for.
* \param target The target to be searched for.
* \param workload_name The name of the workload to be searched for.
* \return The best record of the given workload; std::nullopt if not found.
*/
using FQueryTuningRecord = ffi::TypedFunction<ffi::Optional<TuningRecord>(
const IRModule&, const Target&, const ffi::String&)>;
/*!
* \brief The function type of `QuerySchedule` method.
* \param mod The IRModule to be searched for.
* \param target The target to be searched for.
* \param workload_name The name of the workload to be searched for.
* \return The schedule in the best schedule of the given workload; std::nullopt if not found.
*/
using FQuerySchedule = ffi::TypedFunction<ffi::Optional<s_tir::Schedule>(
const IRModule&, const Target&, const ffi::String&)>;
/*!
* \brief The function type of `QueryIRModule` method.
* \param mod The IRModule to be searched for.
* \param target The target to be searched for.
* \param workload_name The name of the workload to be searched for.
* \return The IRModule in the best IRModule of the given workload; std::nullopt if not found.
*/
using FQueryIRModule = ffi::TypedFunction<ffi::Optional<IRModule>(const IRModule&, const Target&,
const ffi::String&)>;
/*!
* \brief The function type of `Size` method.
* \return The size of the database.
*/
using FSize = ffi::TypedFunction<int64_t()>;
/*! \brief The packed function to the `HasWorkload` function. */
FHasWorkload f_has_workload;
/*! \brief The packed function to the `CommitWorkload` function. */
FCommitWorkload f_commit_workload;
/*! \brief The packed function to the `CommitTuningRecord` function. */
FCommitTuningRecord f_commit_tuning_record;
/*! \brief The packed function to the `GetTopK` function. */
FGetTopK f_get_top_k;
/*! \brief The packed function to the `GetAllTuningRecords` function. */
FGetAllTuningRecords f_get_all_tuning_records;
/*! \brief The packed function to the `QueryTuningRecord` function. */
FQueryTuningRecord f_query_tuning_record;
/*! \brief The packed function to the `QuerySchedule` function. */
FQuerySchedule f_query_schedule;
/*! \brief The packed function to the `QueryIRModule` function. */
FQueryIRModule f_query_ir_module;
/*! \brief The packed function to the `Size` function. */
FSize f_size;
static void RegisterReflection() {
// ffi::Functions are all not registered, because the reflection system doesn't take care of
// them, so it cannot be accessible on the python side. If there is such need from the future,
// we can then add corresponding accessor methods to help access on python.
// `f_has_workload` is not registered
// `f_commit_workload` is not registered
// `f_commit_tuning_record` is not registered
// `f_get_top_k` is not registered
// `f_get_all_tuning_records` is not registered
// `f_query_tuning_record` is not registered
// `f_query_schedule` is not registered
// `f_query_ir_module` is not registered
// `f_size` is not registered
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<PyDatabaseNode>();
}
bool HasWorkload(const IRModule& mod) final {
TVM_FFI_ICHECK(f_has_workload != nullptr) << "PyDatabase's HasWorkload method not implemented!";
return f_has_workload(mod);
}
Workload CommitWorkload(const IRModule& mod) final {
TVM_FFI_ICHECK(f_commit_workload != nullptr)
<< "PyDatabase's CommitWorkload method not implemented!";
return f_commit_workload(mod);
}
void CommitTuningRecord(const TuningRecord& record) final {
TVM_FFI_ICHECK(f_commit_tuning_record != nullptr)
<< "PyDatabase's CommitTuningRecord method not implemented!";
f_commit_tuning_record(record);
}
ffi::Array<TuningRecord> GetTopK(const Workload& workload, int top_k) final {
TVM_FFI_ICHECK(f_get_top_k != nullptr) << "PyDatabase's GetTopK method not implemented!";
return f_get_top_k(workload, top_k);
}
ffi::Array<TuningRecord> GetAllTuningRecords() final {
TVM_FFI_ICHECK(f_get_all_tuning_records != nullptr)
<< "PyDatabase's GetAllTuningRecords method not implemented!";
return f_get_all_tuning_records();
}
ffi::Optional<TuningRecord> QueryTuningRecord(const IRModule& mod, const Target& target,
const ffi::String& workload_name) final {
if (f_query_tuning_record == nullptr) {
return DatabaseNode::QueryTuningRecord(mod, target, workload_name);
} else {
return f_query_tuning_record(mod, target, workload_name);
}
}
ffi::Optional<s_tir::Schedule> QuerySchedule(const IRModule& mod, const Target& target,
const ffi::String& workload_name) final {
if (f_query_schedule == nullptr) {
return DatabaseNode::QuerySchedule(mod, target, workload_name);
} else {
return f_query_schedule(mod, target, workload_name);
}
}
ffi::Optional<IRModule> QueryIRModule(const IRModule& mod, const Target& target,
const ffi::String& workload_name) final {
if (f_query_ir_module == nullptr) {
return DatabaseNode::QueryIRModule(mod, target, workload_name);
} else {
return f_query_ir_module(mod, target, workload_name);
}
}
int64_t Size() final {
TVM_FFI_ICHECK(f_size != nullptr) << "PyDatabase's Size method not implemented!";
return f_size();
}
static constexpr const bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("s_tir.meta_schedule.PyDatabase", PyDatabaseNode, DatabaseNode);
};
/*!
* \brief Managed reference to DatabaseNode.
* \sa DatabaseNode
*/
class Database : public ffi::ObjectRef {
public:
/*!
* \brief Constructor from ffi::ObjectPtr<DatabaseNode>.
* \param data The object pointer.
*/
explicit Database(ffi::ObjectPtr<DatabaseNode> data) : ffi::ObjectRef(data) {
TVM_FFI_ICHECK(data != nullptr);
}
/*!
* \brief An in-memory database.
* \param mod_eq_name A string to specify the module equality testing and hashing method.
*/
TVM_DLL static Database MemoryDatabase(ffi::String mod_eq_name = "structural");
/*!
* \brief A database for injecting handcrafted schedule functions.
* \param schedule_fn The function to do scheduling, which takes a TIR schedule,
* and returns a boolean indicating if the schedule is successful.
* \param mod_eq_name A string to specify the module equality testing and hashing method.
*/
TVM_DLL static Database ScheduleFnDatabase(ffi::TypedFunction<bool(s_tir::Schedule)> schedule_fn,
ffi::String mod_eq_name = "structural");
/*!
* \brief Create a default database that uses JSON file for tuning records.
* \param path_workload The path to the workload table.
* \param path_tuning_record The path to the database table.
* \param allow_missing Whether to create new file when the given path is not found.
* \param mod_eq_name A string to specify the module equality testing and hashing method.
*/
TVM_DLL static Database JSONDatabase(ffi::String path_workload, ffi::String path_tuning_record,
bool allow_missing, ffi::String mod_eq_name = "structural");
/*!
* \brief A database composed of multiple databases, allowing users to guide IR rewriting using
* combined knowledge of those databases. To each query, it returns the best record among all the
* databases given.
* \param databases The list of databases to be combined.
* \return The combined database.
*/
TVM_DLL static Database UnionDatabase(ffi::Array<Database, void> databases);
/*!
* \brief A database composed of multiple databases, allowing users to guide IR rewriting using
* combined knowledge of those databases. To each query, it returns the record from the first
* database that responds to the query.
* \param databases The database to be subsetted.
* \return The subsetted database.
*/
TVM_DLL static Database OrderedUnionDatabase(ffi::Array<Database, void> databases);
/*!
* \brief Create a database with customized methods on the python-side.
* \param f_has_workload The packed function of `HasWorkload`.
* \param f_commit_workload The packed function of `CommitWorkload`.
* \param f_commit_tuning_record The packed function of `CommitTuningRecord`.
* \param f_get_top_k The packed function of `GetTopK`.
* \param f_get_all_tuning_records The packed function of `GetAllTuningRecords`.
* \param f_query_tuning_record The packed function of `QueryTuningRecord`.
* \param f_query_schedule The packed function of `QuerySchedule`.
* \param f_query_ir_module The packed function of `QueryIRModule`.
* \param f_size The packed function of `Size`.
* \param mod_eq_name A string to specify the module equality testing and hashing method.
* \return The created database.
*/
TVM_DLL static Database PyDatabase(PyDatabaseNode::FHasWorkload f_has_workload,
PyDatabaseNode::FCommitWorkload f_commit_workload,
PyDatabaseNode::FCommitTuningRecord f_commit_tuning_record,
PyDatabaseNode::FGetTopK f_get_top_k,
PyDatabaseNode::FGetAllTuningRecords f_get_all_tuning_records,
PyDatabaseNode::FQueryTuningRecord f_query_tuning_record,
PyDatabaseNode::FQuerySchedule f_query_schedule,
PyDatabaseNode::FQueryIRModule f_query_ir_module,
PyDatabaseNode::FSize f_size,
ffi::String mod_eq_name = "structural");
/*! \return The current Database in the scope. */
static ffi::Optional<Database> Current();
/*! \brief Entering the scope of the context manager */
void EnterWithScope();
/*! \brief Exiting the scope of the context manager */
void ExitWithScope();
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Database, ffi::ObjectRef, DatabaseNode);
};
} // namespace meta_schedule
} // namespace s_tir
} // namespace tvm
#endif // TVM_S_TIR_META_SCHEDULE_DATABASE_H_