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

645 lines
20 KiB
Python

# 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.
# ruff: noqa: RUF012
"""TuningRecord database"""
from collections.abc import Callable
from typing import Any, Optional, Union
# isort: off
from typing import Literal
# isort: on
from tvm_ffi import register_object
from tvm.ir.module import IRModule
from tvm.runtime import Object
from tvm.s_tir.schedule import Schedule, Trace
from tvm.target import Target
from .. import _ffi_api
from ..arg_info import ArgInfo
from ..utils import _json_de_tvm
@register_object("s_tir.meta_schedule.Workload")
class Workload(Object):
"""A workload, i.e. an IRModule and its structural hash.
Parameters
----------
mod : IRModule
The workload's IRModule
"""
mod: IRModule
def __init__(self, mod: IRModule) -> None:
self.__init_handle_by_constructor__(
_ffi_api.Workload, # type: ignore # pylint: disable=no-member
mod,
)
def as_json(self) -> Any:
"""Export the workload to JSON as a python object.
Returns
-------
json : Any
The JSON serialized as a python object (e.g. a Dict or List).
Use json.dumps() to get the associated json string.
"""
return _json_de_tvm(_ffi_api.WorkloadAsJSON(self)) # type: ignore # pylint: disable=no-member
@staticmethod
def from_json(json_obj: Any) -> "Workload":
"""Create a workload from a json object.
Parameters
----------
json_obj : Any
The json object to parse.
Returns
-------
tuning_record : TuningRecord
The parsed tuning record.
"""
return _ffi_api.WorkloadFromJSON(json_obj) # type: ignore # pylint: disable=no-member
@register_object("s_tir.meta_schedule.TuningRecord")
class TuningRecord(Object):
"""The class of tuning records.
Parameters
----------
trace : tvm.ir.Trace
The trace of the tuning record.
workload : Workload
The workload of the tuning record.
run_secs : Optional[List[float]]
The run time of the tuning record.
target : Optional[Target]
The target of the tuning record.
args_info : Optional[List[ArgInfo]]
The argument information of the tuning record.
"""
trace: Trace
workload: Workload
run_secs: list[float] | None
target: Target | None
args_info: list[ArgInfo] | None
def __init__( # type: ignore # pylint: disable=too-many-arguments
self,
trace: Trace,
workload: Workload,
run_secs: list[float] | None = None,
target: Target | None = None,
args_info: list[ArgInfo] | None = None,
) -> None:
self.__init_handle_by_constructor__(
_ffi_api.TuningRecord, # type: ignore # pylint: disable=no-member
trace,
workload,
run_secs,
target,
args_info,
)
def as_measure_candidate(self) -> Any:
"""Generate a measure candidate given an initial IR module and a trace
stored in the tuning record.
Returns
-------
candidate : MeasureCandidate
A generated candidate.
"""
return _ffi_api.TuningRecordAsMeasureCandidate(self) # type: ignore # pylint: disable=no-member
def as_json(self) -> Any:
"""Export the tuning record to a JSON string.
Returns
-------
json_str : str
The JSON string exported.
"""
return _json_de_tvm(_ffi_api.TuningRecordAsJSON(self)) # type: ignore # pylint: disable=no-member
@staticmethod
def from_json(json_obj: Any, workload: Workload) -> "TuningRecord":
"""Create a tuning record from a json object.
Parameters
----------
json_obj : Any
The json object to parse.
workload : Workload
The workload.
Returns
-------
tuning_record : TuningRecord
The parsed tuning record.
"""
return _ffi_api.TuningRecordFromJSON(json_obj, workload) # type: ignore # pylint: disable=no-member
@register_object("s_tir.meta_schedule.Database")
class Database(Object):
"""The abstract database interface."""
DatabaseType = Union["Database", Literal["json", "memory"]]
def has_workload(self, mod: IRModule) -> bool:
"""Check if the database has the given workload.
Parameters
----------
mod : IRModule
The IRModule to be searched for.
Returns
-------
result : bool
Whether the database has the given workload.
"""
return _ffi_api.DatabaseHasWorkload(self, mod) # type: ignore # pylint: disable=no-member
def commit_workload(self, mod: IRModule) -> Workload:
"""Commit a workload to the database if missing.
Parameters
----------
mod : IRModule
The IRModule to be searched for or added.
Returns
-------
workload : Workload
The workload corresponding to the given IRModule.
"""
return _ffi_api.DatabaseCommitWorkload(self, mod) # type: ignore # pylint: disable=no-member
def commit_tuning_record(self, record: TuningRecord) -> None:
"""Commit a tuning record to the database.
Parameters
----------
record : TuningRecord
The tuning record to add.
"""
_ffi_api.DatabaseCommitTuningRecord(self, record) # type: ignore # pylint: disable=no-member
def get_top_k(self, workload: Workload, top_k: int) -> list[TuningRecord]:
"""Get the top K valid tuning records of given workload from the database.
Parameters
----------
workload : Workload
The workload to be searched for.
top_k : int
The number of top records to get.
Returns
-------
top_k_records : List[TuningRecord]
The top K records.
"""
return _ffi_api.DatabaseGetTopK(self, workload, top_k) # type: ignore # pylint: disable=no-member
def get_all_tuning_records(self) -> list[TuningRecord]:
"""Get all the tuning records from the database.
Returns
-------
tuning_records : List[TuningRecord]
All tuning records from the database.
"""
return _ffi_api.DatabaseGetAllTuningRecords(self) # type: ignore # pylint: disable=no-member
def __len__(self) -> int:
"""Get the number of records in the database.
Returns
-------
num_records : int
The number of records in the database
"""
return _ffi_api.DatabaseSize(self) # type: ignore # pylint: disable=no-member
def query_tuning_record(
self,
mod: IRModule,
target: Target,
workload_name: str,
) -> TuningRecord | None:
"""Query the best record of the given workload from the database.
Parameters
----------
mod : IRModule
The IRModule to be searched for.
target : Target
The target to be searched for.
workload_name : str
The name of the workload to be searched for.
Returns
-------
tuning_record : Optional[TuningRecord]
The best record of the given workload; None if not found.
"""
return _ffi_api.DatabaseQueryTuningRecord(self, mod, target, workload_name) # type: ignore # pylint: disable=no-member
def query_schedule(
self,
mod: IRModule,
target: Target,
workload_name: str,
) -> Schedule | None:
"""Query the best schedule of the given workload from the database.
Parameters
----------
mod : IRModule
The IRModule to be searched for.
target : Target
The target to be searched for.
workload_name : str
The name of the workload to be searched for.
Returns
-------
schedule : Optional[tvm.s_tir.Schedule]
The best schedule of the given workload; None if not found.
"""
return _ffi_api.DatabaseQuerySchedule(self, mod, target, workload_name) # type: ignore # pylint: disable=no-member
def query_ir_module(
self,
mod: IRModule,
target: Target,
workload_name: str,
) -> IRModule | None:
"""Query the best IRModule of the given workload from the database.
Parameters
----------
mod : IRModule
The IRModule to be searched for.
target : Target
The target to be searched for.
workload_name : str
The name of the workload to be searched for.
Returns
-------
ir_module : Optional[IRModule]
The best IRModule of the given workload; None if not found.
"""
return _ffi_api.DatabaseQueryIRModule(self, mod, target, workload_name) # type: ignore # pylint: disable=no-member
def dump_pruned(self, destination: "Database") -> None:
"""Dump the pruned database to files of JSONDatabase format.
Parameters
----------
destination : Database
The destination database to be dumped to.
"""
return _ffi_api.DatabaseDumpPruned( # type: ignore # pylint: disable=no-member
self, destination
)
def query(
self,
mod: IRModule,
target: Target,
*,
workload_name: str = "main",
kind: Literal["schedule"] | Literal["record"] | Literal["ir_module"] = "schedule",
) -> Schedule | IRModule | TuningRecord:
"""Query the database to retrieve the best optimization outcome of the given workload.
Parameters
----------
mod : IRModule
The IRModule to be searched for.
target : Target
The target to be searched for.
kind : str = "schedule" | "record" | "ir_module"
The kind of the optimization outcome to be returned.
Returns
-------
result : Union[tvm.s_tir.Schedule, IRModule, TuningRecord]
The best optimization outcome of the given workload.
"""
if kind == "schedule":
return self.query_schedule(mod, target, workload_name)
if kind == "record":
return self.query_tuning_record(mod, target, workload_name)
if kind == "ir_module":
return self.query_ir_module(mod, target, workload_name)
raise ValueError(f'Unknown kind: {kind}. Candidates are: "schedule", "record", "ir_module"')
def __enter__(self) -> "Database":
"""Entering the scope of the context manager"""
_ffi_api.DatabaseEnterWithScope(self) # type: ignore # pylint: disable=no-member
return self
def __exit__(self, ptype, value, trace) -> None:
"""Exiting the scope of the context manager"""
_ffi_api.DatabaseExitWithScope(self) # type: ignore # pylint: disable=no-member
@staticmethod
def current() -> Optional["Database"]:
"""Get the current database under scope."""
return _ffi_api.DatabaseCurrent() # type: ignore # pylint: disable=no-member
@staticmethod
def create( # pylint: disable=keyword-arg-before-vararg
kind: (
Literal["json", "memory", "union", "ordered_union"] | Callable[[Schedule], bool]
) = "json",
*args,
**kwargs,
) -> "Database":
"""Create a Database.
Parameters
----------
kind : str = "json" | "memory" | "union" | "ordered_union" | Callable[[tvm.s_tir.Schedule],
bool]
The kind of the database to be created. The following kinds are supported:
"json", "memory", "union", "ordered_union", and a custom schedule function.
Returns
-------
database : Database
The created database.
"""
from . import ( # pylint: disable=import-outside-toplevel
JSONDatabase,
MemoryDatabase,
OrderedUnionDatabase,
ScheduleFnDatabase,
UnionDatabase,
)
if callable(kind):
return ScheduleFnDatabase(kind, *args, **kwargs) # type: ignore
if kind == "json":
return JSONDatabase(*args, **kwargs)
if kind == "memory":
return MemoryDatabase(*args, **kwargs) # type: ignore
if kind == "union":
return UnionDatabase(*args, **kwargs) # type: ignore
if kind == "ordered_union":
return OrderedUnionDatabase(*args, **kwargs) # type: ignore
raise ValueError(f"Unknown Database: {kind}")
create = Database.create # pylint: disable=invalid-name
@register_object("s_tir.meta_schedule.PyDatabase")
class _PyDatabase(Database):
"""
A TVM object database to support customization on the python side.
This is NOT the user facing class for function overloading inheritance.
See also: PyDatabase
"""
def __init__(
self,
f_has_workload: Callable | None = None,
f_commit_workload: Callable | None = None,
f_commit_tuning_record: Callable | None = None,
f_get_top_k: Callable | None = None,
f_get_all_tuning_records: Callable | None = None,
f_query_tuning_record: Callable | None = None,
f_query_schedule: Callable | None = None,
f_query_ir_module: Callable | None = None,
f_size: Callable | None = None,
module_equality: str = "structural",
):
"""Constructor."""
self.__init_handle_by_constructor__(
_ffi_api.DatabasePyDatabase, # type: ignore # pylint: disable=no-member
f_has_workload,
f_commit_workload,
f_commit_tuning_record,
f_get_top_k,
f_get_all_tuning_records,
f_query_tuning_record,
f_query_schedule,
f_query_ir_module,
f_size,
module_equality,
)
class PyDatabase:
"""
An abstract database with customized methods on the python-side.
This is the user facing class for function overloading inheritance.
Note: @derived_object is required for proper usage of any inherited class.
"""
_tvm_metadata = {
"cls": _PyDatabase,
"methods": [
"has_workload",
"commit_workload",
"commit_tuning_record",
"get_top_k",
"get_all_tuning_records",
"query_tuning_record",
"query_schedule",
"query_ir_module",
"__len__",
],
}
def has_workload(self, mod: IRModule) -> bool:
"""Check if the database has the given workload.
Parameters
----------
mod : IRModule
The IRModule to be searched for.
Returns
-------
result : bool
Whether the database has the given workload.
"""
raise NotImplementedError
def commit_workload(self, mod: IRModule) -> Workload:
"""Commit a workload to the database if missing.
Parameters
----------
mod : IRModule
The IRModule to be searched for or added.
Returns
-------
workload : Workload
The workload corresponding to the given IRModule.
"""
raise NotImplementedError
def commit_tuning_record(self, record: TuningRecord) -> None:
"""Commit a tuning record to the database.
Parameters
----------
record : TuningRecord
The tuning record to add.
"""
raise NotImplementedError
def get_top_k(self, workload: Workload, top_k: int) -> list[TuningRecord]:
"""Get the top K tuning records of given workload from the database.
Parameters
----------
workload : Workload
The workload to be searched for.
top_k : int
The number of top records to get.
Returns
-------
top_k_records : List[TuningRecord]
The top K records.
"""
raise NotImplementedError
def get_all_tuning_records(self) -> list[TuningRecord]:
"""Get all the tuning records from the database.
Returns
-------
tuning_records : List[TuningRecord]
All tuning records from the database.
"""
raise NotImplementedError
def query_tuning_record(
self, mod: IRModule, target: Target, workload_name: str | None = None
) -> TuningRecord | None:
"""Query a tuning record from the database.
Parameters
----------
mod : IRModule
The IRModule to be searched for.
target : Target
The target to be searched for.
workload_name : Optional[str]
The workload name to be searched for.
Returns
-------
record : Optional[TuningRecord]
The tuning record corresponding to the given workload.
"""
# Using self._outer to replace the self pointer
return _ffi_api.DatabaseQueryTuningRecord( # type: ignore # pylint: disable=no-member
self._outer(),
mod,
target,
workload_name, # type: ignore # pylint: disable=no-member
)
def query_schedule(
self, mod: IRModule, target: Target, workload_name: str | None = None
) -> Schedule | None:
"""Query a schedule from the database.
Parameters
----------
mod : IRModule
The IRModule to be searched for.
target : Target
The target to be searched for.
workload_name : Optional[str]
The workload name to be searched for.
Returns
-------
schedule : Optional[Schedule]
The schedule corresponding to the given workload.
"""
# Using self._outer to replace the self pointer
return _ffi_api.DatabaseQuerySchedule( # type: ignore # pylint: disable=no-member
self._outer(),
mod,
target,
workload_name, # type: ignore # pylint: disable=no-member
)
def query_ir_module(
self, mod: IRModule, target: Target, workload_name: str | None = None
) -> IRModule | None:
"""Query an IRModule from the database.
Parameters
----------
mod : IRModule
The IRModule to be searched for.
target : Target
The target to be searched for.
workload_name : Optional[str]
The workload name to be searched for.
Returns
-------
mod : Optional[IRModule]
The IRModule corresponding to the given workload.
"""
# Using self._outer to replace the self pointer
return _ffi_api.DatabaseQueryIRModule( # type: ignore # pylint: disable=no-member
self._outer(),
mod,
target,
workload_name, # type: ignore # pylint: disable=no-member
)
def __len__(self) -> int:
"""Get the number of records in the database.
Returns
-------
num_records : int
The number of records in the database
"""
raise NotImplementedError