Skip to content

Conversation

alex-weaver
Copy link
Contributor

@alex-weaver alex-weaver commented Nov 23, 2017

I've ported the functions from build_module.py over to C++ to allow compiling from C++; this required also implementing equivalents of Target and BuildConfig in C++. I've also added EXPORT directives to a minimal set of functions in schedule.h to allow implementing the Getting Started example. I would imagine in future most of schedule.h will need EXPORT directives to allow specifying more elaborate schedules from C++.

This implementation can be verified with the following port of the Getting Started example:

#include <tvm\operation.h>
#include <tvm\compilation.h>


using namespace tvm;
using namespace tvm::compilation;

int main()
{
	auto n = tvm::Variable::make(tvm::Int(32), "n");
	tvm::Array<tvm::Expr> shape;
	shape.push_back(n);

	auto A = tvm::placeholder(shape, tvm::Float(32), "A");
	auto B = tvm::placeholder(shape, tvm::Float(32), "B");

	auto C = tvm::compute(A->shape, [&A, &B](tvm::Expr i) {
		return A[i] + B[i];
	}, "C");

	auto s = tvm::create_schedule({ C->op });
	

	auto cAxis = GetAttrHandle<Array<IterVar>>(C->op, "axis");

	tvm::IterVar bx, tx;
	s[C].split(cAxis[0], 64, &bx, &tx);

	s[C].bind(bx, tvm::thread_axis(tvm::Range(), "blockIdx.x"));
	s[C].bind(tx, tvm::thread_axis(tvm::Range(), "threadIdx.x"));
	

	auto args = Array<Tensor>({ A, B, C });
	std::unordered_map<Tensor, Buffer> binds;

	BuildConfig config;
	auto target = target_cuda();
	auto targetHost = default_target_host(target);

	auto lowered = Lower(s, args, "func", binds, config);
	auto module = BuildModule(Array<LoweredFunc>({ lowered }), target, targetHost, config);

	auto dev_module = module->imports()[0];
	std::cout << dev_module->GetSource("");

    return 0;
}

@tqchen tqchen self-requested a review November 23, 2017 18:48
@tqchen
Copy link
Member

tqchen commented Nov 23, 2017

Thanks for starting this. Since the C++ api headers is quite serious change, there are a few things that we need to fix in this PR. Here are some guidelines

  • Code style
    • Use stl_case for variables and structure members
    • Use CamelCase for internal functions
    • Mark inline for functions in header files
      • Aoid doing functions in the header files unless they are strictly short.
  • User facing API
    • Use stl_case for the user facing functions(put them under tvm namespace)
    • This include lower and build
    • Make the function signature as consistent as possible with python counterparts, as eventually we can expose these functions

* \file compilation.h
* \brief Functions for compiling ops.
*/
#ifndef TVM_COMPILATION_H_
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the same name tvm/build_module.h


namespace tvm {

namespace compilation {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leave user facing API and data structure under root namespace.

std::unordered_set<std::string> options;


Target(std::string targetName, DLDeviceType deviceType, int max_num_threads,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a static function to construct from target string

*/
struct Target {
/*! \brief The name of the target device */
std::string targetName;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use stl_case for variable name

/*! \brief The type of the target device */
DLDeviceType deviceType;
/*! \brief The maximum threads that a schedule should use for this device */
int max_num_threads;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set default value of the optional argument here



/*! \brief Convenience function for getting attributes */
TVMValue GetAttr(NodeRef node, std::string attrName) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function do not belong to here. Instead of using GetAttr, use

node.as<Expr>->type() for the type


/*! \brief Convenience function for getting handle attributes */
template<typename T>
T GetAttrHandle(NodeRef node, std::string attrName) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the node.as<> instead of C API mechanism to get the children

* \param config The build configuration.
* \return The built Stmt.
*/
EXPORT Stmt BuildStmt(Schedule sch, Array<Tensor> args, std::unordered_map<Tensor, Buffer> binds,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a private function that should belong to cc file

* \param config The build configuration.
* \return The lowered function.
*/
EXPORT LoweredFunc Lower(Schedule sch, Array<Tensor> args, std::string name,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lower->lower as it is user facing, make API function consistent with python API. Pass by const reference when possible if the function do not move the content

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returns Array

* \param config The build configuration.
* \return The built module.
*/
EXPORT runtime::Module BuildModule(Array<LoweredFunc> funcs, const Target& target,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BuildModule->build

@alex-weaver
Copy link
Contributor Author

Ok hopefully that commit resolves the style issues - naming conventions should now be observed and the API surface in the header should be significantly reduced. There's a couple of things I'm not sure about though:

  1. You mentioned lower() should return array - the python version of lower() does not return array - is this correct?
  2. You requested a static function to construct a Target from a string - is this intended to just switch on the name and call the appropriate function in target:: or should it parse out options too?

@tqchen
Copy link
Member

tqchen commented Nov 24, 2017

  • The main reason is that the python version of build is be able to accept both schedule/ array of LoweredFunc and LoweredFunc.
    • We want result of lower to be directly feedable to the build function, so as a compromise here, we can have lower return a list of array for now.
  • It should be consistent with the Target parsing behavior on python side

@alex-weaver
Copy link
Contributor Author

Ok lower() now returns array, and there is a function Target::create which should be consistent with create() in target.py

Copy link
Member

@tqchen tqchen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for being patient with this, here is another set of review comments


/*!
* \brief Container for target device information.
* Use target_llvm, target_cuda etc functions instead of constructing directly.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update comment to target::llvm

#define TVM_BUILD_MODULE_H_

#include <string>
#include "./tvm/c_dsl_api.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is no longer needed

EXPORT static Target create(const std::string& target_str);
};

namespace target {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

document the namespace


Target(const std::string& target_name, DLDeviceType device_type, int max_num_threads,
int thread_warp_size, const std::unordered_set<std::string>& keys,
const std::unordered_set<std::string>& options) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use member initializers for class members.


namespace tvm {

/*!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

general style issue: Google C style requires 2-space indentation.

@@ -0,0 +1,317 @@
/*!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put the file under src/codegen/build_module.cc for now, since for now it is only stich up things and do not need a new module

* Compile executable modules.
* \file build_module.cc
*/
#include "./tvm/build_module.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use <tvm/build_module.h> as it is in include path, same for all other includes

} else if (x->func_type == kDeviceFunc) {
fdevice.push_back(x);
} else {
CHECK(false) << "unknown function type " << x->func_type;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOG(FATAL)

stmt = ir::StorageRewrite(stmt);
stmt = ir::UnrollLoop(stmt, config.auto_unroll_max_step, config.auto_unroll_max_depth,
config.auto_unroll_max_extent, config.unroll_explicit);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one blank line



// Phase 2

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to insert blank line here

@alex-weaver
Copy link
Contributor Author

No problem, I've fixed up the requested changes

@tqchen
Copy link
Member

tqchen commented Nov 28, 2017

please fix the lint error, and add an unittest-case to tests/cpp

}

void GetBinds(const Array<Tensor>& args, const std::unordered_map<Tensor, Buffer>& binds,
Map<Tensor, Buffer>* out_binds, Array<NodeRef>* out_arg_list,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normally function argument are aligned to last bracket, reference other code.

@alex-weaver
Copy link
Contributor Author

OK I've added a unit test, but I'm not sure how to get the c++ tests to build with CUDA support? I've tried changing the Jenkinsfile but that didn't seem to help - is it using the Jenkinsfile from the commit or from the current master? Or is this not the right way to enable CUDA for C++ tests?

@tqchen
Copy link
Member

tqchen commented Nov 29, 2017

for now let us write the testcase with only LLVM (cpu) as target

@anchepiece
Copy link

I just want to say good work @alex-weaver this is great.

Copy link
Member

@tqchen tqchen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few last things to work on, which hopefully will fix the test


TEST(BuildModule, Basic) {
using namespace tvm;
auto n = Variable::make(Int(32), "n");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Var("n")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can only use Var("n") if the constructor of Halide::VarExpr is marked EXPORT - is this OK? I was originally using Variable::make so as not to require exposing the implementation detail from the Halide namespace

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I think we can create a function var, which constructs Var and export that function, so it is consistent with python

} // namespace target

bool LLVMEnabled() {
const runtime::PackedFunc* pf = runtime::Registry::Get("codegen.llvm_target_enabled");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is not correctly implemented, refer to https://github.com/dmlc/tvm/blob/master/src/codegen/codegen.cc#L24

@tqchen
Copy link
Member

tqchen commented Dec 1, 2017

please fix my other comment about LLVMEnabled not being correctly implemented

Changed implementation of LLVMEnabled()
@alex-weaver
Copy link
Contributor Author

Ok requested fixes are implemented, but it seems the issue with running the test is that the C++ test environment is not built with LLVM support. Is there a simple way to get this enabled for the C++ tests?

@tqchen tqchen merged commit 0ef2928 into apache:master Dec 5, 2017
@tqchen
Copy link
Member

tqchen commented Dec 5, 2017

thanks for being patience on improving the pr, this is now merged.

@alex-weaver
Copy link
Contributor Author

@tqchen great :) btw would it also be helpful to port some of the python schedules in TOPI to C++ too? I can look at that soon if it would be useful.

@tqchen tqchen mentioned this pull request May 29, 2018
5 tasks
tqchen pushed a commit to tqchen/tvm that referenced this pull request Jul 6, 2018
* Port build_module.py to C++

* Fix lint errors

* Fix more lint errors

* Fix more lint errors

* Fix more lint errors

* Fix build error

* Implemented style fixes

* Fix lint errors

* Added function to construct target from string
lower now returns array

* Fix lint error

* Implemented review changes - style & Target options -> std::vector

* Fixed lint, argument alignment and added unit test

* Changed test to target LLVM, fixed sign compare warnings

* Reverted unit test to CUDA, changed Jenkinsfile to enable GPU for C++ tests

* Slight change to Jenkinsfile

* Changed build_module test from CUDA to LLVM

* Added function var() to construct a Var instance.
Changed implementation of LLVMEnabled()

* Reverted Jenkinsfile
sergei-mironov pushed a commit to sergei-mironov/tvm that referenced this pull request Aug 8, 2018
* Port build_module.py to C++

* Fix lint errors

* Fix more lint errors

* Fix more lint errors

* Fix more lint errors

* Fix build error

* Implemented style fixes

* Fix lint errors

* Added function to construct target from string
lower now returns array

* Fix lint error

* Implemented review changes - style & Target options -> std::vector

* Fixed lint, argument alignment and added unit test

* Changed test to target LLVM, fixed sign compare warnings

* Reverted unit test to CUDA, changed Jenkinsfile to enable GPU for C++ tests

* Slight change to Jenkinsfile

* Changed build_module test from CUDA to LLVM

* Added function var() to construct a Var instance.
Changed implementation of LLVMEnabled()

* Reverted Jenkinsfile
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants