blob: 8028809c8ce0a595a10bfb9480264db1b72e750f [file] [log] [blame]
# Copyright 2021 The IREE Authors
#
# Licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# iree_mlir_benchmark_suite()
#
# Generates benchmark suites for MLIR input modules. The generated artifacts
# will be placed in the "<binary-root>/benchmark_suites/<category>" directory,
# where "<category>" is the name of the immediate directory containing the
# CMakeLists.txt. The generated artifacts are expected to be executed with
# `iree-benchmark-module`.
#
# Parameters:
# MODULES: A list for model specification. Due to CMake's lack of nested list
# support, all model's specification is put in the same list, where each
# model takes six consecutive elements for the following information:
# - MODULE_NAMES: The input module's name.
# - MODULE_TAGS: A list of comma-separated tags for the input module.
# - MLIR_SOURCES: The input file for each input module. It can be a file
# checked in the repository; it can also be a URL for downloading
# from the web. When it's a URL, the file should be a a direct .mlir
# file or a tarball containing a .mlir file; for both cases, the .mlir
# file should have a name matching the one in MODULE_NAMES.
# - ENTRY_FUNCTIONS: The entry function name for the input module.
# - FUNCTION_INPUTS: A list of comma-separated entry function inputs for
# the input module.
# BENCHMARK_MODES: A list strings, where ech one of them is a comma-
# separated list of benchmark mode tags.
# TARGET_BACKEND: The compiler target backend.
# TARGET_ARCHITECTURE: The detailed target backend's architecture.
# TRANSLATION_FLAGS: A list of command-line options and their values to
# pass to the IREE translation tool for artifact generation.
# DRIVER: The runtime driver.
# RUNTIME_FLAGS: A list of command-line options and their values to pass
# to the IREE runtime during benchmark exectuion.
#
# The above parameters largely fall into two categories: 1) for specifying
# the MLIR input module and its metadata, 2) for specifying the translation/
# runtime configuration.
#
# 1)
#
# MODULE_NAMES, MODULE_TAGS, MLIR_SOURCES, ENTRY_FUNCTIONS, and FUNCTION_INPUTS
# together provide good flexiblity for specifying the MLIRinput module and its
# metadata. For example, we can generate modules with idential name from
# different sources (TensorFlow, TFLite, PyTorch, etc.), and we can transform
# the same input module differently for benchmarking different aspects like
# fp32 vs fp16.
#
# 2)
#
# TARGET_BACKEND and TRANSLATION_FLAGS control how the input module will be
# converted into the final IREE deployable module format. DRIVER and
# RUNTIME_FLAGS specify how the module will be executed. BENCHMARK_MODES
# can be used to give descriptions of the translation/runtime configuration
# (e.g., full-inference vs. kernel-execution) and specify more contextual
# requirements (e.g., big-core vs. little-core).
#
function(iree_mlir_benchmark_suite)
if(NOT IREE_BUILD_BENCHMARKS)
return()
endif()
cmake_parse_arguments(
PARSE_ARGV 0
_RULE
""
"DRIVER;TARGET_BACKEND;TARGET_ARCHITECTURE"
"BENCHMARK_MODES;MODULES;TRANSLATION_FLAGS;RUNTIME_FLAGS"
)
# All fields' names for each module.
set(_FIELD_NAMES "_MODULE_NAME" "_MODULE_TAGS"
"_MLIR_SOURCE" "_ENTRY_FUNCTION" "_FUNCTION_INPUTS")
list(LENGTH _FIELD_NAMES _FIELD_COUNT)
math(EXPR _MAX_FIELD_INDEX "${_FIELD_COUNT} - 1")
# Make sure we have some multiple of six elements.
list(LENGTH _RULE_MODULES _MODULE_TOTAL_ELEMENT_COUNT)
math(EXPR _MODULE_COUNT
"${_MODULE_TOTAL_ELEMENT_COUNT} / ${_FIELD_COUNT}")
math(EXPR _MODULE_ELEMENT_REMAINDER
"${_MODULE_TOTAL_ELEMENT_COUNT} % ${_FIELD_COUNT}")
if(NOT ${_MODULE_ELEMENT_REMAINDER} EQUAL 0)
message(SEND_ERROR "MODULES expected to have some multiple of six "
"elements; some module has missing/redundant fields.")
endif()
# Loop over all modules to create targets.
math(EXPR _MAX_MODULE_INDEX "${_MODULE_COUNT} - 1")
foreach(_MODULE_INDEX RANGE 0 "${_MAX_MODULE_INDEX}")
# Loop over all elements for the current module and assign them to the
# corresponding field names for later use.
foreach(_FIELD_INDEX RANGE 0 "${_MAX_FIELD_INDEX}")
list(GET _FIELD_NAMES ${_FIELD_INDEX} _FIELD_NAME)
math(EXPR _INDEX "${_MODULE_INDEX} * ${_FIELD_COUNT} + ${_FIELD_INDEX}")
list(GET _RULE_MODULES ${_INDEX} ${_FIELD_NAME})
endforeach()
# Use the last directory's name as the category.
get_filename_component(_CATEGORY "${CMAKE_CURRENT_SOURCE_DIR}" NAME)
# Generate all benchmarks to the root build directory. This helps for
# discovering them and execute them on devices.
set(_ROOT_ARTIFACTS_DIR "${IREE_BINARY_DIR}/benchmark_suites/${_CATEGORY}")
set(_VMFB_ARTIFACTS_DIR "${_ROOT_ARTIFACTS_DIR}/vmfb")
# The source file used to generate benchmark artifacts.
set(_SOURCE_FILE "${_MLIR_SOURCE}")
# The CMake target's name if we need to download from the web.
set(_DOWNLOAD_TARGET_NAME "")
# If the source file is from the web, create a custom command to download it.
# And wrap that with a custom target so later we can use for dependency.
#
# Note: We actually should not do this; instead, we should directly compile
# from the initial source (i.e., TensorFlow Python models). But that is
# tangled with the pending Python testing infrastructure revamp so we'd prefer
# to not do that right now.
if("${_MLIR_SOURCE}" MATCHES "^https?://")
# Update the source file to the downloaded-to place.
string(REPLACE "/" ";" _SOURCE_URL_SEGMENTS "${_MLIR_SOURCE}")
# TODO: we can do `list(POP_BACK _SOURCE_URL_SEGMENTS _LAST_URL_SEGMENT)`
# after migrating to CMake 3.15.
list(LENGTH _SOURCE_URL_SEGMENTS _URL_SEGMENT_COUNT)
math(EXPR _SEGMENT_LAST_INDEX "${_URL_SEGMENT_COUNT} - 1")
list(GET _SOURCE_URL_SEGMENTS ${_SEGMENT_LAST_INDEX} _LAST_URL_SEGMENT)
set(_DOWNLOAD_TARGET_NAME "iree-download-benchmark-source-${_LAST_URL_SEGMENT}")
string(REPLACE "tar.gz" "mlir" _FILE_NAME "${_LAST_URL_SEGMENT}")
set(_SOURCE_FILE "${_ROOT_ARTIFACTS_DIR}/${_MODULE_NAME}.mlir")
if (NOT TARGET "${_DOWNLOAD_TARGET_NAME}")
add_custom_command(
OUTPUT "${_SOURCE_FILE}"
COMMAND
"${Python3_EXECUTABLE}" "${IREE_ROOT_DIR}/scripts/download_file.py"
"${_MLIR_SOURCE}" -o "${_ROOT_ARTIFACTS_DIR}"
DEPENDS
"${IREE_ROOT_DIR}/scripts/download_file.py"
COMMENT "Downloading ${_MLIR_SOURCE}"
)
add_custom_target("${_DOWNLOAD_TARGET_NAME}"
DEPENDS "${_SOURCE_FILE}"
)
endif()
endif()
# Next create the command and target for compiling the input module into
# IREE deployable format for each benchmark mode.
string(JOIN "-" _MODULE_DIR_NAME "${_MODULE_NAME}" "${_MODULE_TAGS}")
foreach (_BENCHMARK_MODE IN LISTS _RULE_BENCHMARK_MODES)
set(_BENCHMARK_DIR_NAME
"iree-${_RULE_DRIVER}__${_RULE_TARGET_ARCHITECTURE}__${_BENCHMARK_MODE}")
# A list of name segments for composing unique CMake target names.
set(_COMMON_NAME_SEGMENTS "${_MODULE_NAME}")
string(REPLACE "," "-" _TAGS "${_MODULE_TAGS}")
string(REPLACE "," "-" _MODE "${_BENCHMARK_MODE}")
list(APPEND _COMMON_NAME_SEGMENTS
"${_TAGS}" "${_MODE}" "${_RULE_TARGET_BACKEND}"
"${_RULE_TARGET_ARCHITECTURE}")
# The full list of translation flags.
set(_TRANSLATION_ARGS "--iree-mlir-to-vm-bytecode-module")
list(APPEND _TRANSLATION_ARGS "--iree-hal-target-backends=${_RULE_TARGET_BACKEND}")
list(SORT _RULE_TRANSLATION_FLAGS)
list(APPEND _TRANSLATION_ARGS ${_RULE_TRANSLATION_FLAGS})
# Get a unique identifier for this IREE module file by hashing the command
# line flags and input file. We will also use this for the CMake target.
string(SHA1 _VMFB_HASH "${_TRANSLATION_ARGS};${_SOURCE_FILE}")
set(_TRANSLATION_TARGET_NAME "iree-generate-benchmark-artifact-${_VMFB_HASH}")
# Register the target once and share across all benchmarks having the same
# MLIR source and translation flags.
if(NOT TARGET "${_TRANSLATION_TARGET_NAME}")
set(_VMFB_FILE "${_VMFB_ARTIFACTS_DIR}/compiled-${_VMFB_HASH}.vmfb")
add_custom_command(
OUTPUT "${_VMFB_FILE}"
COMMAND
"$<TARGET_FILE:iree_tools_iree-translate>"
${_TRANSLATION_ARGS}
"${_SOURCE_FILE}"
-o "${_VMFB_FILE}"
WORKING_DIRECTORY "${_VMFB_ARTIFACTS_DIR}"
DEPENDS
iree_tools_iree-translate
"${_DOWNLOAD_TARGET_NAME}"
COMMENT "Generating VMFB for ${_COMMON_NAME_SEGMENTS}"
)
add_custom_target("${_TRANSLATION_TARGET_NAME}"
DEPENDS "${_VMFB_FILE}"
)
# Mark dependency so that we have one target to drive them all.
add_dependencies(iree-benchmark-suites "${_TRANSLATION_TARGET_NAME}")
endif(NOT TARGET "${_TRANSLATION_TARGET_NAME}")
# Add a friendly target alias for this particular benchmark.
set(_FRIENDLY_TARGET_NAME_LIST "iree-generate-benchmark-artifact")
list(APPEND _FRIENDLY_TARGET_NAME_LIST ${_COMMON_NAME_SEGMENTS})
list(JOIN _FRIENDLY_TARGET_NAME_LIST "__" _FRIENDLY_TARGET_NAME)
add_custom_target("${_FRIENDLY_TARGET_NAME}"
DEPENDS "${_TRANSLATION_TARGET_NAME}"
)
# Finally create the command and target for the flagfile used to execute the
# generated artifacts.
set(_FLAGFILE_ARTIFACTS_DIR "${_ROOT_ARTIFACTS_DIR}/${_MODULE_DIR_NAME}/${_BENCHMARK_DIR_NAME}")
set(_FLAG_FILE "${_FLAGFILE_ARTIFACTS_DIR}/flagfile")
set(_ADDITIONAL_ARGS_CL "--additional_args=\"${_RULE_RUNTIME_FLAGS}\"")
add_custom_command(
OUTPUT "${_FLAG_FILE}"
COMMAND
"${Python3_EXECUTABLE}" "${IREE_ROOT_DIR}/scripts/generate_flagfile.py"
--module_file="../../vmfb/compiled-${_VMFB_HASH}.vmfb"
--driver=${_RULE_DRIVER}
--entry_function=${_ENTRY_FUNCTION}
--function_inputs=${_FUNCTION_INPUTS}
"${_ADDITIONAL_ARGS_CL}"
-o "${_FLAG_FILE}"
DEPENDS
"${IREE_ROOT_DIR}/scripts/generate_flagfile.py"
WORKING_DIRECTORY "${_FLAGFILE_ARTIFACTS_DIR}"
COMMENT "Generating ${_FLAG_FILE}"
)
set(_FLAGFILE_GEN_TARGET_NAME_LIST "iree-generate-benchmark-flagfile")
list(APPEND _FLAGFILE_GEN_TARGET_NAME_LIST ${_COMMON_NAME_SEGMENTS})
list(JOIN _FLAGFILE_GEN_TARGET_NAME_LIST "__" _FLAGFILE_GEN_TARGET_NAME)
add_custom_target("${_FLAGFILE_GEN_TARGET_NAME}"
DEPENDS "${_FLAG_FILE}"
)
# Mark dependency so that we have one target to drive them all.
add_dependencies(iree-benchmark-suites "${_FLAGFILE_GEN_TARGET_NAME}")
endforeach(_BENCHMARK_MODE IN LISTS _RULE_BENCHMARK_MODES)
endforeach(_MODULE_INDEX RANGE 0 "${_MAX_MODULE_INDEX}")
endfunction()