blob: 2076ee7d363c7eaf4f710aaf5d9eb6e12496b35d [file] [log] [blame]
# 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.
"""
TopHub: Tensor Operator Hub
To get the best performance, we typically need auto-tuning for the specific devices.
TVM releases pre-tuned parameters in TopHub for some common networks and hardware targets.
TVM will download these parameters for you when you call relay.build.
"""
# pylint: disable=invalid-name
import logging
import os
import sys
from .task import ApplyHistoryBest
from ..target import Target
from ..contrib.download import download
from .record import load_from_file
from .util import EmptyContext
# environment variable to read TopHub location
AUTOTVM_TOPHUB_LOC_VAR = "TOPHUB_LOCATION"
# default location of TopHub
AUTOTVM_TOPHUB_DEFAULT_LOC = "https://raw.githubusercontent.com/uwsampl/tvm-distro/master/tophub"
# value of AUTOTVM_TOPHUB_LOC_VAR to specify to not read from TopHub
AUTOTVM_TOPHUB_NONE_LOC = "NONE"
# root path to store TopHub files
AUTOTVM_TOPHUB_ROOT_PATH = os.path.join(os.path.expanduser("~"), ".tvm", "tophub")
# the version of each package
PACKAGE_VERSION = {
"arm_cpu": "v0.07",
"llvm": "v0.04",
"cuda": "v0.09",
"rocm": "v0.05",
"opencl": "v0.04",
"mali": "v0.06",
"intel_graphics": "v0.02",
"vta": "v0.09",
"amd_apu": "v0.01",
}
logger = logging.getLogger("autotvm")
def _alias(name):
"""convert alias for some packages"""
table = {
"vtacpu": "vta",
"metal": "opencl",
"webgpu": "opencl",
"vulkan": "opencl",
"nvptx": "cuda",
"amd_apu": "amd_apu",
}
return table.get(name, name)
def _get_tophub_location():
location = os.getenv(AUTOTVM_TOPHUB_LOC_VAR, None)
return AUTOTVM_TOPHUB_DEFAULT_LOC if location is None else location
def context(target, extra_files=None):
"""Return the dispatch context with pre-tuned parameters.
This function will load the corresponding *.log files in AUTOTVM_TOPHUB_ROOT_PATH.
If cannot find them, it will download them from TopHub github repo.
Users can also add their own files in argument `extra_files`.
Parameters
----------
target: Target or List of Target
The compilation target
extra_files: list of str, optional
Extra log files to load
"""
tophub_location = _get_tophub_location()
if tophub_location == AUTOTVM_TOPHUB_NONE_LOC:
return EmptyContext()
best_context = ApplyHistoryBest([])
targets = target if isinstance(target, (list, tuple)) else [target]
for tgt in targets:
if isinstance(tgt, str):
tgt = Target(tgt)
possible_names = []
device = tgt.attrs.get("device", "")
if device != "":
possible_names.append(_alias(device))
possible_names.append(tgt.kind.name)
all_packages = list(PACKAGE_VERSION.keys())
for name in possible_names:
name = _alias(name)
if name in all_packages:
if not check_backend(tophub_location, name):
continue
filename = "%s_%s.log" % (name, PACKAGE_VERSION[name])
best_context.load(os.path.join(AUTOTVM_TOPHUB_ROOT_PATH, filename))
break # only load one file to avoid some fallback template mismatch problem
if extra_files:
for filename in extra_files:
best_context.load(filename)
return best_context
def check_backend(tophub_location, backend):
"""Check whether have pre-tuned parameters of the certain target.
If not, will download it.
Parameters
----------
backend: str
The name of backend.
Returns
----------
success: bool
Whether the check is successful.
"""
backend = _alias(backend)
assert backend in PACKAGE_VERSION, 'Cannot find backend "%s" in TopHub' % backend
version = PACKAGE_VERSION[backend]
package_name = "%s_%s.log" % (backend, version)
if os.path.isfile(os.path.join(AUTOTVM_TOPHUB_ROOT_PATH, package_name)):
return True
# pylint: disable=import-outside-toplevel
if sys.version_info >= (3,):
import urllib.request as urllib2
else:
import urllib2
try:
download_package(tophub_location, package_name)
return True
except urllib2.URLError as e:
logging.warning("Failed to download tophub package for %s: %s", backend, e)
return False
def download_package(tophub_location, package_name):
"""Download pre-tuned parameters of operators for a backend
Parameters
----------
tophub_location: str
The location to download TopHub parameters from
package_name: str
The name of package
"""
rootpath = AUTOTVM_TOPHUB_ROOT_PATH
if not os.path.isdir(rootpath):
# make directory
splits = os.path.split(rootpath)
for j in range(1, len(splits) + 1):
path = os.path.join(*splits[:j])
if not os.path.isdir(path):
os.mkdir(path)
download_url = "{0}/{1}".format(tophub_location, package_name)
logger.info("Download pre-tuned parameters package from %s", download_url)
download(download_url, os.path.join(rootpath, package_name), True, verbose=0)
# global cache for load_reference_log
REFERENCE_LOG_CACHE = {}
def load_reference_log(backend, model, workload_name):
"""Load reference log from TopHub to support fallback in template.
Template will use these reference logs to choose fallback config.
Parameters
----------
backend: str
The backend name
model: str
The name of the device model
workload_name: str
The name of the workload. (The first item in the workload tuple)
"""
backend = _alias(backend)
version = PACKAGE_VERSION[backend]
package_name = "%s_%s.log" % (backend, version)
filename = os.path.join(AUTOTVM_TOPHUB_ROOT_PATH, package_name)
global REFERENCE_LOG_CACHE
key = (backend, model, workload_name)
if key not in REFERENCE_LOG_CACHE:
tmp = []
# If TOPHUB_LOCATION is not AUTOTVM_TOPHUB_NONE_LOC,
# Download the config file from tophub if not exists.
if not os.path.exists(filename):
tophub_location = _get_tophub_location()
if tophub_location != AUTOTVM_TOPHUB_NONE_LOC:
download_package(tophub_location, package_name)
if os.path.isfile(filename): # in case download failed
find = False
inp = None
counts = {}
for inp, res in load_from_file(filename):
counts[inp.target.model] = counts.get(inp.target.model, 0) + 1
if model == inp.target.model:
find = True
break
# if device model is not find, use the device model with the most tuned workloads
if not find and counts:
model = max(counts.items(), key=lambda k: k[1])[0]
for inp, res in load_from_file(filename):
if model == inp.target.model and inp.task.workload[0] == workload_name:
tmp.append((inp, res))
REFERENCE_LOG_CACHE[key] = tmp
return REFERENCE_LOG_CACHE[key]