blob: ae9b364d93482890eaa9e038a955665f7df6795d [file] [log] [blame]
// Copyright 2019 Google LLC
//
// Licensed 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
//
// https://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 THIRD_PARTY_MLIR_EDGE_IREE_HAL_EXECUTABLE_CACHE_H_
#define THIRD_PARTY_MLIR_EDGE_IREE_HAL_EXECUTABLE_CACHE_H_
#include "third_party/mlir_edge/iree/base/bitfield.h"
#include "third_party/mlir_edge/iree/base/ref_ptr.h"
#include "third_party/mlir_edge/iree/base/status.h"
#include "third_party/mlir_edge/iree/base/wait_handle.h"
#include "third_party/mlir_edge/iree/hal/executable.h"
#include "third_party/mlir_edge/iree/hal/executable_format.h"
#include "third_party/mlir_edge/iree/hal/executable_spec.h"
namespace iree {
namespace hal {
// Defines how the executable cache performs preparation.
enum class ExecutableCachingMode : uint32_t {
// Allows the cache to reference the provided executable_data after it has
// prepared the executable. Callers must ensure the data remains valid for the
// lifetime of the cache. If memory mapping constant executable data from
// disk this can be used to avoid copies.
kAliasProvidedData = 1 << 0,
// Allows the prepared executable to be cached persistently (on disk/etc).
// Enable for any executable that is likely to be used in future runs.
// Note that not all caches support persistent serialization and this is just
// a hint.
kAllowPersistentCaching = 1 << 1,
// Allows the cache to optimize the executable as much as it can.
// This may cause preparation to take significantly longer while (hopefully)
// improving runtime performance. Avoid for one-shot executables.
kAllowOptimization = 1 << 2,
// Enables Executable debugging methods if supported by the device and
// executable. This may disable certain optimizations or retain additional
// data to allow disassembly, stepping, etc.
//
// Device must support the DeviceFeature::kDebugging feature and executables
// must support the ExecutableFeature::kDebugging feature.
kEnableDebugging = 1 << 3,
// Enables Executable coverage if supported by the device and executable.
// Depending on the optimization mode this may produce partial coverage
// results (for example, when certain source operations were optimized away).
//
// Device must support the DeviceFeature::kCoverage feature and executables
// must support the ExecutableFeature::kCoverage feature.
kEnableCoverage = 1 << 4,
// Enables Executable profiling if supported by the device and executable.
// Depending on the optimization mode this may produce partial profiling
// results. Profiling attribution (whether to the entire executable or
// specific operations) depends on the implementation.
//
// Device must support the DeviceFeature::kProfiling feature and executables
// must support the ExecutableFeature::kProfiling feature.
kEnableProfiling = 1 << 5,
// Default caching mode.
kDefault = kAllowPersistentCaching | kAllowOptimization,
};
IREE_BITFIELD(ExecutableCachingMode);
using ExecutableCachingModeBitfield = ExecutableCachingMode;
// A cache of prepared executables for a particular device.
// Caches may be shared across multiple devices from the same driver or specific
// to individual devices. Caches may persist prepared executables across process
// launches or reprepare them each run. Callers should assume that the cache is
// a no-op and the returned Executables only live for as long as the cache does.
//
// The term 'cache' here is rather optimistic - it's perfectly acceptable for
// implementations to not cache at all and return new Executables for each
// PrepareExecutable called (even for the same executable). Callers should
// expect such behavior and try to retain the results of the PrepareExecutable
// calls to reduce overhead in re-preparing executables.
//
// Thread-safe - multiple threads may prepare executables (including the *same*
// executable) simultaneously.
class ExecutableCache {
public:
virtual ~ExecutableCache();
// TODO(benvanik): status/queries (size, etc).
// TODO(b/137153339): serialization/deserialization.
// Returns true if the executable cache can prepare the given executable input
// format. Perparation may still fail if the particular version or features
// required by the executable are not supported.
virtual bool CanPrepareFormat(ExecutableFormat format) const = 0;
// Prepares an executable for use.
// The provided |spec| and |executable_data| will be used to either lookup a
// previously prepared executable in the cache or prepare a new one.
//
// Depending on the driver preparation may take a non-trivial amount of time
// (such as when JITing/etc). As the cache is internally synchronized callers
// can issue preparation requests from multiple threads - even for the same
// executables - and calls will block until preparation completes.
//
// When preparing a large number of executables it's recommended to use the
// PrepareExecutables method to batch and wait on the results.
virtual StatusOr<ref_ptr<Executable>> PrepareExecutable(
ExecutableCachingModeBitfield mode, const ExecutableSpec& spec) = 0;
// Prepares one or more executables asynchronously on a worker thread (maybe).
// When the WaitHandle is signaled successfully |out_executables| will contain
// one Executable for each ExecutableSpec provided in |specs|, in order.
// The backing memory of |out_executables| must remain valid until the
// WaitHandle resolves. Preparation errors will be returned on the WaitHandle.
// If more than one preparation errors occurs only one will be returned (from
// an undefined order).
//
// Note: not all implementations will actually perform preparation
// asynchronously. This method just allows drivers to do so as possible.
//
// If applications already have their own preparation threads it is better to
// use PrepareExecutable in a loop to avoid the creation of new threads.
virtual StatusOr<WaitHandle> PrepareExecutables(
ExecutableCachingModeBitfield mode,
absl::Span<const ExecutableSpec> specs,
absl::Span<ref_ptr<Executable>> out_executables);
protected:
ExecutableCache();
};
} // namespace hal
} // namespace iree
#endif // THIRD_PARTY_MLIR_EDGE_IREE_HAL_EXECUTABLE_CACHE_H_