DSL Function Debugging

Overview

The following guides you through the function debugging procedure on the CPU to check whether the algorithm logic of an operator is correct if an error occurs during operator execution.

When developing TBE operators in DSL mode, you simply need to pay attention to the algorithm logic, as the operator scheduling can be automatically completed using the auto_schedule API provided by the TBE DSL. If the algorithm logic description is proved correct but the operator execution error persists, the error might be an internal TBE error. In this case, you can submit an issue in the Ascend open-source community.

Procedure

The TBE DSL provides an efficient debugging framework for verifying operator functions on the CPU. The procedure is as follows.

Figure 1 DSL function debugging workflow
  1. Enter the debug mode and obtain the operator running context.
    1. Enter the debug mode by calling the tbe.common.testing.debug API and using Python's with statement. Then, select the CPU as the running platform of the operator.
    2. Obtain the operator context by calling tbe.common.testing.get_ctx.
  2. Define golden data for subsequent intermediate tensor verification and operator output verification.
  3. Implement the compute logic of the DSL operator and verifies intermediate data as required.
    1. Use the DSL API provided by TBE to describe the compute logic, which specifies the computation method and procedure of the operator.
    2. (Optional) Verify intermediate tensor data.

      Call the tbe.common.testing.print_tensor API to save the intermediate tensor data to a file or print the data to the screen, and call the tbe.common.testing.assert_allclose API to verify the intermediate tensor data.

  4. Call the create_schedule API of TVM to create a scheduling instance object of the operator.
  5. Build and run the operator.
    1. Call the tbe.common.testing.build API to build the DSL operator running on the CPU.
    2. Call the tbe.common.testing.run API to execute the operator.
  6. Verify the output data.

    The debugging code of the DSL operator is complete. You can write an entry point function and call operator APIs to debug operator functionalities.

Debugging Example

This example shows how to verify the intermediate tensor. a and b are input tensors, which are used to do the calculation: d = a + b + b. Calculate c = a + b, and then calculate d = c + b, where c is the intermediate tensor.

from tbe import tvm
from tbe import dsl
from tbe.common.utils import para_check
from tbe.common.utils import shape_util
# Import the API related to the testing module.
from tbe.common.testing.testing import *
import numpy as np

@para_check.check_input_type(dict, dict, dict, str)
def addtest(input_a, input_b, output_d, kernel_name="addtest"):
    # Enter the DSL debug mode and select the CPU as the running platform.
    with debug(): 
        # Obtain the operator running context.
        ctx = get_ctx()

        # Obtain the shape and dtype of input data.
        shape_a = shape_util.scalar2tensor_one(input_a.get("shape"))
        shape_b = shape_util.scalar2tensor_one(input_b.get("shape"))
        data_type = input_a.get("dtype").lower()

        # Define the size of the input golden data using NumPy.
        a = tvm.nd.array(np.random.uniform(size=shape_a).astype(data_type), ctx)
        b = tvm.nd.array(np.random.uniform(size=shape_b).astype(data_type), ctx)
        # Initialize the output d to all 0s using NumPy.
        d = tvm.nd.array(np.zeros(shape_a, dtype=data_type), ctx)
        
        # Call the placeholder API of TVM to create a placeholder for each input tensor, returning a tensor object, respectively.
        data_a = tvm.placeholder(shape_a, name="data_1", dtype=data_type)
        data_b = tvm.placeholder(shape_b, name="data_2", dtype=data_type)
        # Call the DSL compute API to implement data_a + data_b.
        data_c = dsl.vadd(data_a, data_b)
	
        # Verify the intermediate tensor.
        sample = open('samplefile.txt', 'w')
        # Save the intermediate tensor data_c to the samplefile.txt file.
        print_tensor(data_c, ofile=sample)
        # Check whether the value of data_c is correct.
        assert_allclose(data_c, desired=a.asnumpy() + b.asnumpy(), tol=[1e-7, 1e-7])
        print("The value of data_c is the same as the expected value.")
					
	# Continue to customize the compute logic of the DSL operator and call the DSL API to implement: data_d = data_c + data_b.
        data_d = dsl.vadd(data_c, data_b)
        # Call the create_schedule API of TVM to create a scheduling instance object of the operator. Pass the op list of the output tensor as the argument.
        s = tvm.create_schedule(data_d.op)

        # Perform building to generate an operator. [data_a, data_b, data_d] is the placeholder for the input and output lists, and AddTest is the name of the custom operator.
        build(s, [data_a, data_b, data_d], name="AddTest")           

        # Execute the operator. Pass the arguments a, b, and d in sequence to the AddTest operator.
        run(a, b, d)  # AddTest(a, b, d)

        # Print the value of the output d and compare it with the expected result to check whether they are consistent.
        print("d:", d)
        tvm.testing.assert_allclose(d.asnumpy(), a.asnumpy() + b.asnumpy() + b.asnumpy())
        print("The actual output is the same as the expected output.")

# Write an entry point function and call the addtest function.
if __name__ == "__main__":
    input_output_dict = {"shape": (2, 3, 4),"format": "ND","ori_shape": (2, 3, 4),"ori_format": "ND", "dtype":"float32"}
    addtest(input_output_dict, input_output_dict, input_output_dict, kernel_name="addtest")

The following is output to the screen:

======================== debug enter =======================
The value of data_c is the same as the expected value.
Tensor add_0 is saved to file samplefile.txt.
d: [[[1.1099341  2.7536283  0.6441797  1.9604567 ]
  [0.25995332 1.997332   1.4089121  1.7263429 ]
  [0.59042984 1.079533   2.1142702  1.3301892 ]]

 [[1.6043677  1.2658448  1.7424912  0.57584894]
  [1.6016111  1.2254462  0.34414443 1.43648   ]
  [2.1096592  1.4920356  2.0675716  2.458529  ]]]
The actual output is the same as the expected output.
======================== debug exit ========================

According to the preceding print information "The value of data_c is the same as the expected value.", the calculation result of the intermediate tensor data_c is the same as the expected result.

According to the preceding print information "The actual output is the same as the expected output.", the output is the same as the expected result.

The data of the intermediate tensor data_c is saved to the samplefile.txt file.