Ansor log file 格式
log file 的每一行是一个 json,其格式为:
这些变量对应的类型为:
inputs: MeasureInput
MeasureInput 类型头文件 MeasureInputNode 类型头文件
读写 MeasureInputNode 的 log 时调用的函数
input 部分的格式为:
task 和 state 对应的类型分别为 SearchTask 和 State
task:SearchTask
SearchTask 类型头文件 SearchTaskNode 类型头文件
task 部分的格式为:
[
workload_key,
target,
hardware_params,
target_host, // if define else ""
layout_rewrite_option,
task_input_names
]
涉及变量的类型为:
String workload_key;
Target target;
HardwareParams hardware_params;
Target target_host;
LayoutRewriteOption layout_rewrite_option;
Array<String> task_input_names;
workload_key:String
workload_key 的生成见 make_workload_key.
workload_key 字符串格式为:
[func_name, [args...]]
workload_key 格式为
如果这个子任务是用户自己注册的函数,那么 func_name 就是函数名。
若是 TVM 从完整模型中抽取出的子任务,那么 func_name 是用该子任务的 DAG 计算出的 hash 值,默认是 hashlib.md5(str_dag).hexdigest(),其中 str_dag 字符串是由 ComputeDAG::PrintDAG 产生,形如:
p0 = PLACEHOLDER
p1 = PLACEHOLDER
T_matmul_NT(i, j) += (p0[i, k]*p1[j, k])
p2 = PLACEHOLDER
T_add(ax0, ax1) = (T_matmul_NT(ax0, ax1) + p2[ax0, ax1])
target:Target
target 部分的格式为(其中 attrs 中每个元素都会转换为 -{key}={value} 格式的字符串):
hardware_params:HardwareParams
HardwareParams 类型头文件 HardwareParamsNode 类型头文件
读写HardwareParamsNode的log时调用的函数
该部分格式为:
[
num_cores,
vector_unit_bytes,
cache_line_bytes,
max_shared_memory_per_block,
max_local_memory_per_block,
max_threads_per_block,
max_vthread_extent,
warp_size
]
对应变量的类型都是 int。
layout_rewrite_option:LayoutRewriteOption
layout_rewrite_option 类型 LayoutRewriteOption 是个 enum class
enum class LayoutRewriteOption : int {
NoRewrite = 0,
InsertTransformStage = 1,
RewriteForPreTransformed = 2,
};
state:State
state 部分的格式为:
对应变量的类型如下:
stages: Array<Stage>
即 stages 部分就是个空数组。
transform_steps:Array<Step>
该部分的 json 格式为:(每个 step_1 是一个 Step 对应的 json)
由于 StepNode::WriteToRecord 是一个虚函数,因此输出 json 时调用的函数 WriteToRecord 的定义要在 StepNode 的子类(如 AnnotationStepNode、FuseStepNode、PragmaStepNode 等)中寻找。
在 include/tvm/auto_scheduler/transform_step.h 中搜索 public StepNode 能找到都有哪些子类,这些子类的 WriteToRecord 的实现在 src/auto_scheduler/transform_step.cc 中。
results:MeasureResult
MeasureResult 类型头文件 MeasureResultNode 类型头文件
读写 MeasureResultNode 的log时调用的函数
result 部分输出格式:
涉及变量的类型:
error_no 值的含义见 MeasureErrorNO(cpp)或 MeasureErrorNo(python 或 python),其中 0 代表无错误,4 代表运行时错误,6 代表编译超时,7代表运行超时。
其中 costs 是指执行时间,而 all_cost 指整个 measure 的耗时。
在同一服务器上多个 GPU 上同时进行 TVM tuning 时会出现的问题
如果在同一台服务器上运行多个 TVM tuning 任务,measure 过程中很容易发生编译超时的错误,从而导致 tuning 过程中进行的有效 measure 非常少。
这是因为,一旦编译时间超过设定的 time_out,TVM 会自动 kill 掉编译进程,并将该实现标记为 error_no=6。通过查看 TVM 源码,发现 TVM 默认的编译并行度 n_parallel 是设置为 multiprocessing.cpu_count(),即系统所有的 CPU 逻辑核数。因此,当 TVM tuning 无法使用全部的 CPU 内核,或者服务器中包含可使用全部 CPU core 的多个 TVM tuning 时,算子的编译时间会变得非常长,以至于被 TVM 误判为编译超时。
在逻辑核数为 56 的服务器上,设置 docker container 的
cpuset_cpus,使得 TVM tuning 仅可使用 8 个核,结果导致 tuning 过程中最多有 3/4 的 measure 都是error_no=6。
解决办法(两者取其一):
- 修改 TVM 源码,将源码中所有
multiprocessing.cpu_count()替换为len(os.sched_getaffinity(0))。
sed -i -e '1i\import os' -e 's/multiprocessing.cpu_count()/len(os.sched_getaffinity(0))/g' $(grep -rl multiprocessing.cpu_count "$TVM_PATH/python")
- 调低 TVM tuner 的并行度,或者调高超时阈值:
# 修改并行度
builder = auto_scheduler.LocalBuilder(n_parallel=len(os.sched_getaffinity(0)))
# 修改超时阈值
# builder = auto_scheduler.LocalBuilder(timeout=60) # 默认是15
tuner = auto_scheduler.TaskScheduler(tasks, task_weights, ...)
tune_option = auto_scheduler.TuningOptions(
builder=builder
# ...=...
)
tuner.tune(tune_option)
如何从 log 中复用调度
目标:从 log 的中读取 input 部分的 transform_steps,生成对应的 cuda 代码,然后在新的目标硬件上进行测试生成对应 result。
[Measure] 过程调用的 C++ 函数:SilentMeasure(task, inputs, results),即 verbose >= 1 时,输出 ........******** 类似输出的时候,调用的函数。
该 C++ 函数的 build 部分(即编译部分)调用 python 函数 local_builder_build,而运行部分调用 python 函数 local_run (如果 使用的是 rpc runner,那么运行部分调用的是 rpc_runer_run
inputs = []
for log_string in logs_list:
measure_input, old_result = tvm.auto_scheduler.measure_record.load_record_from_string(log_string)
inputs.append(measure_input)
n_parallel = 8
build_results = tvm.auto_scheduler.measure.local_builder_build(inputs, timeout, n_parallel)
number=3
repeat=1
min_repeat_ms=100
results = tvm.auto_scheduler.measure.local_run(inputs, build_results, timeout, number, repeat, min_repeat_ms)
测试结果:第一个很慢,慢启动