Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extend documentation: add_new_kernel #125

Merged
merged 6 commits into from
Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![Codecov](https://codecov.io/gh/clEsperanto/CLIc_prototype/branch/master/graph/badge.svg?token=QRSZHYDFIF)](https://codecov.io/gh/clEsperanto/CLIc_prototype)
[![License](https://img.shields.io/badge/license-BSD-informational)](https://github.com/clEsperanto/CLIc_prototype/blob/master/LICENSE)
[![CppStd](https://img.shields.io/badge/cpp--std-c%2B%2B17-blue)](https://en.cppreference.com/w/cpp/17)
[![OpenCL](https://img.shields.io/badge/OpenCL-1.2%20%7C%202.%202%20%7C3.0-green)](https://www.khronos.org/opencl/)
[![OpenCL](https://img.shields.io/badge/OpenCL-1.2%20%7C%202.2%20%7C%203.0-green)](https://www.khronos.org/opencl/)
[![Website](https://img.shields.io/website?url=http%3A%2F%2Fclesperanto.net)](http://clesperanto.net)
[![GitHub issues](https://img.shields.io/github/issues-raw/clEsperanto/CLIc_prototype)](https://github.com/clEsperanto/CLIc_prototype/issues)
[![GitHub stars](https://img.shields.io/github/stars/clEsperanto/CLIc_prototype?style=social)](https://github.com/clEsperanto/CLIc_prototype)
Expand Down Expand Up @@ -58,8 +58,8 @@ Follow the [installation guide](./docs/guidelines.md) for helps on compilation a

Clone the repository and update the submodules
```
git clone git@github.com:clEsperanto/CLIc_prototype.git CLIc
cd CLIc && git submodule update --init
git clone git@github.com:clEsperanto/CLIc_prototype.git
cd CLIc_prototype
```

Create a build folder and configure cmake to generate the adapted makefile.
Expand Down
27 changes: 22 additions & 5 deletions docs/guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,41 @@

## Requierements

1. A C++ compiler
1. A C++ compiler (e.g. gcc, clang, msvc)
2. [CMake](https://cmake.org/download/), version 3.20 or higher.
3. [OpenCL](https://www.khronos.org/opencl/)

## OpenCL installation

CLIc rely on both the `FindOpenCL` package provided by CMake to determine the OpenCL library location on the system and the [Khronos OpenCL headers](https://github.com/KhronosGroup/OpenCL-Headers) for compilation.
Please follow the [instruction to install OpenCL](./opencl_installation.md) before considering using CLIc.
CLIc rely on the OpenCL library to operate. The OpenCL library is provided by the vendor of your GPU. Please follow the [instruction to install OpenCL](./how_to_build/opencl_installation.md) before considering using CLIc.

## Source compilation

|[Windows](./windows_build/windows_build.md)|[Linux](./linux_build/linux_build.md)|[MacOS](./macos_build/macos_build.md)|
|[Windows](./how_to_build/windows_build/windows_build.md)|[Linux](./how_to_build/linux_build/linux_build.md)|[MacOS](./how_to_build/macos_build/macos_build.md)|
|-|-|-|

## Developpeur guidelines

All contribution should be done through forks and pull requests. Please follow the [contribution guidelines](./contribution_guidelines.md) to contribute to CLIc.

Add a new kernels to CLIc:
- Provide an nD opencl kernel to the [clesperanto-kernels](https://github.com/clEsperanto/clij-opencl-kernels/tree/clesperanto_kernels) repository
- Add a new kernel class to CLIc (see [here](./how_to_contribute/kernel_class_creation.md))
- Create the [header file](./how_to_contribute/kernel_class_creation.md#header) for the new kernel class
- Create the [source file](./how_to_contribute/kernel_class_creation.md#source) for the new kernel class
- Provide a `Call` function and a `Gateway method` to operate the new kernel class (see [here](./how_to_contribute/kernel_class_creation.md#call-and-gateway-access))
- Add a test case for the new kernel class (see [here](./how_to_contribute/kernel_test_case.md))
- Create the [source file](./how_to_contribute/kernel_test_case.md#test_source) of the test case
- Add the test case to the [test suite](./how_to_contribute/kernel_test_case.md#test_cmakelist)
- All code should respect the coding style enforced through the use of [clang-format](https://clang.llvm.org/docs/ClangFormat.html). See [here](./how_to_contribute/coding_style.md) for more information.

If you do not know where to start, you can check the [list of kernels](https://github.com/clEsperanto/CLIc_prototype/issues/120) still missing, and do not hesitate to take contact with use through an issue.


## FAQ

**WIP**

## More help?

Do not hesitate to create [an issue](https://github.com/clEsperanto/CLIc_prototype/issues) if you are facing un-documented errors or difficulties.
Do not hesitate to create [an issue](https://github.com/clEsperanto/CLIc_prototype/issues) if you are facing un-documented errors or difficulties. Or via the [clEsperanto](https://forum.image.sc/tags/clesperanto) section of the `image.sc` forum.
File renamed without changes.
80 changes: 80 additions & 0 deletions docs/how_to_contribute/coding_style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Coding Style

## General

For clarity and consistency, we rely on clang-format to format the code. The [configuration file](../../.clang-format) is located in the root of the repository. It is strongly recommended to use the clang-format plugin for your favorite editor, or to use the command line tool.

The formatting rules are strongly inspired by the same rules used in the [ITK](https://github.com/InsightSoftwareConsortium/ITK) Library.

To format the code, run the following command at the root of your repository:

```bash
clang-format -i -style=file $(find . -name "*.cpp" -or -name "*.hpp")
```

If you do not have clang-format, please refer to their [documentations](https://clang.llvm.org/docs/index.html) for installation, or to the following [issue](https://github.com/clEsperanto/CLIc_prototype/issues/116) on how to install it on your computer.

## Naming

### Variables

Variables should be named using `camelCase` convention. For example:

```cpp
int numberOfIterations = 10;
```

### Classes

Classes should be named using `PascalCase` convention. For example:

```cpp
class MyClass
{
};
```

All kernel classes should keep the `Kernel` suffix. For example:

```cpp
class AddImagesKernel : public Operation
{
};
```

### Macros

Macros should be named using `UPPER_CASE` convention. For example:

```cpp
#define MY_MACRO 10
```

### Constants

Constants should be named using `UPPER_CASE` convention. For example:

```cpp
const int MY_CONSTANT = 10;
```

### Namespaces

Namespaces should be named using `lower_case` convention. For example:

```cpp
namespace my_namespace
{
}
```

Only two namespace are currently used in the library, `cle` and `backend`. Those are to differentiate the library code from the backend code (OpenCL).

### Files

Files should be named using the prefix `cle` and the suffix `hpp` or `cpp`. For example:

```bash
cleAddImagesKernel.hpp
cleAddImagesKernel.cpp
```
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Add new kernel to CLIc
# Kernel class creation

All kernel operation in CLIc must inherite from the `Operation` class which define all the major functions needed for running an OpenCL kernel, and must have a valid kernel file `.cl` associated to it. The `Operation` class is defined in [`clic/include/core/cleOperation.hpp`](https://github.com/clEsperanto/CLIc_prototype/blob/master/clic/include/core/cleOperation.hpp) and the source code is in [`clic/src/core/cleOperation.cpp`](https://github.com/clEsperanto/CLIc_prototype/blob/master/clic/src/core/cleOperation.cpp). The kernel files are located in [clij-opencl-kernels](https://github.com/clEsperanto/clij-opencl-kernels/tree/clesperanto_kernels) repository.
All kernel operations in CLIc must inherite from the `Operation` class which define all the major functions needed for running an OpenCL kernel, and must have a valid kernel file `.cl` associated to it. The `Operation` class is defined in [`clic/include/core/cleOperation.hpp`](https://github.com/clEsperanto/CLIc_prototype/blob/master/clic/include/core/cleOperation.hpp) and the source code is in [`clic/src/core/cleOperation.cpp`](https://github.com/clEsperanto/CLIc_prototype/blob/master/clic/src/core/cleOperation.cpp). The kernel files are located in [clij-opencl-kernels](https://github.com/clEsperanto/clij-opencl-kernels/tree/clesperanto_kernels) repository.

## Define a new kernel class

The first step is to define a new class inheriting from `Operation` class, by creating a header file (`.hpp`) and a source file (`.cpp`). The class name must correspond to the kernel name. For example, the `AddImageAndScalarKernel` kernel is defined in [`clic/include/tier1/cleAddImageAndScalarKernel.hpp`]() and the source code is in [`clic/src/tier1/cleAddImageAndScalarKernel.cpp`]().

The operations are grouped in different tiers, defining their complexity. The `tier1` operations are the most basic operations, the `tier2` operations are more complex operations which rely on some `tier1` operations. The `tier3` operations rely a minima on a `tier2` operation. The `tier4` on `tier3` and so on.

### __Header file__
### <a id="header">__Header file__</a>

First we declare the `AddImageAndScalarKernel` class in a header (`.hpp`) file as follow:

Expand Down Expand Up @@ -62,7 +62,7 @@ auto SetScalar(const float & scalar) -> void;

As you can see the header file only hold declaration of class and methods, no actual code is written here. This is the role of the source file.

### __Source file__
### <a id="source">__Source file__</a>

For each header file we need to create a corresponding source file (`.cpp`) with the same name. The source file is where the class methods code are defined.
```cpp
Expand Down Expand Up @@ -120,7 +120,9 @@ auto AddImageAndScalarKernel::SetScalar(const float & scalar) -> void
```
Same as for the `SetInput` and `SetOutput` functions, the `AddParameter` method is called to add the scalar value to the kernel arguments. The `AddParameter` method can be used as well to add also integers.

### __The Kernel Call function__
### <a id="call-and-gateway-access">__Call function and Gateway methaod__</a>

## __The Kernel Call function__

The last step to finalise the class if to declare the `Call` function. This function is the one that will be called to run the kernel. It is named identically as the class with the prefix `_Call` and is defined in the header file as an `inline` function. The `inline` keyword is used to tell the compiler to copy the function code at the place where the function is called. This is done to avoid the overhead of calling a function. The `inline` keyword is not mandatory but it is recommended to use it for the `Call` function.

Expand All @@ -138,6 +140,30 @@ inline auto AddImageAndScalarKernel_Call(const ProcessorPointer & device, const
It allows a quick and simple way to call and run the kernel without having to instanciate the kernel class. The `Call` function takes the same arguments as the kernel class constructor and the `SetInput`, `SetOutput` and `SetScalar` functions. The `Call` function is then responsible to instanciate the kernel class, to set the input and output images and the scalar value and to run the kernel.
In addition to simplying kernel call, it enable simple python wrapper for the `pyclesperanto` package. The drowback is that each call of the `Call` function will instanciate a new kernel class. This is not a problem for small kernels but for large kernels it is better to instanciate the kernel class only once and to call the `Execute` method multiple times.

## Add a new method to the `Clesperanto` class gateway

In order to make the new kernel accessible to the user, we need to add a new method to the `Clesperanto` class gateway.
The `Clesperanto` class is defined in [`clic/include/core/clesperanto.hpp`](https://github.com/clEsperanto/CLIc_prototype/blob/master/clic/include/core/clesperanto.hpp) and the source code is in [`clic/src/core/clesperanto.cpp`](https://github.com/clEsperanto/CLIc_prototype/blob/master/clic/src/core/clesperanto.cpp).

### Modify the header file

Add the new kernel as a method to the header (`clic/include/core/clesperanto.hpp`):
```cpp
auto
AddImageAndScalar(const Image & source, const Image & destination, const float & scalar = 0) -> void;
```

### Modify the source file

And call `AddImageAndScalarKernel_Call` from within this method in (`clic/src/core/clesperanto.cpp`):
```cpp
auto
Clesperanto::AddImageAndScalar(const Image & source, const Image & destination, const float & scalar) -> void
{
AddImageAndScalarKernel_Call(this->GetDevice(), source, destination, scalar);
}
```

### __Summary__

We have now a fully implemented kernel class in the CLIc library. The next step is to make the kernel accessible to the user by adding a new method to the `Clesperanto` class gateway and to provide a valid test case to insure that the kernel is working as expected.
Now that the kernel is accessible to the user, it is very important to [**provide a valid test case**](https://github.com/clEsperanto/CLIc_prototype/blob/extend_doc_add_new_kernel/docs/add_new_kernel/add_test_case.md) to insure that the kernel is working as expected.
88 changes: 88 additions & 0 deletions docs/how_to_contribute/kernel_test_case.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Add a valid test case

In order to ensure that the kernel is working as expected, write a test case and register it in `tests/CMakeLists.txt`.

## <a id="test_source">Write a test case</a>

In this example, add `tests/add_image_and_scalar_test.cpp`. In order to be able to generate random test data, we include `<random>`. Obviously, we also need to include `clesperanto.hpp`.

```cpp
#include <random>

#include "clesperanto.hpp"

template <class type>
auto
```
The test routine itself is defined in `run_test`, which accepts two parameters: `shape` and `mem_type`. In this case, the test routine simply tests whether the kernel manages to add a scalar (10) to a constant vector filled with the value 10.

```cpp
run_test(const std::array<size_t, 3> & shape, const cle::MemoryType & mem_type) -> bool
{
const type value = 10;
const type scalar = 10;
std::vector<type> input(shape[0] * shape[1] * shape[2]);
std::vector<type> valid(shape[0] * shape[1] * shape[2]);
std::fill(input.begin(), input.end(), static_cast<type>(value));
std::fill(valid.begin(), valid.end(), static_cast<type>(value + scalar));

cle::Clesperanto cle;
cle.GetDevice()->WaitForKernelToFinish();
auto gpu_input = cle.Push<type>(input, shape, mem_type);
auto gpu_output = cle.Create<type>(shape, mem_type);
cle.AddImageAndScalar(gpu_input, gpu_output, scalar);
auto output = cle.Pull<type>(gpu_output);

return std::equal(output.begin(), output.end(), valid.begin());
}
```
The main function executes `run_test` with vectors of various shapes:
```cpp
auto
main(int argc, char ** argv) -> int
{
if (!run_test<float>({ 10, 1, 1 }, cle::BUFFER))
{
return EXIT_FAILURE;
}

if (!run_test<signed int>({ 10, 1, 1 }, cle::BUFFER))
{
return EXIT_FAILURE;
}
\\ many more different shapes are tested
}
```

## <a id="test_cmakelist">Register the test</a>

In `tests/CMakeLists.txt`, we need to register the text in three places:

In the beginning, we register the executable, its dependencies and define targets:
```makefile
add_executable(add_image_and_scalar_test add_image_and_scalar_test.cpp)
add_dependencies(add_image_and_scalar_test CLIc)
target_link_libraries(add_image_and_scalar_test PRIVATE CLIc::CLIc)
set_target_properties(add_image_and_scalar_test PROPERTIES FOLDER "Tests")
target_compile_features(add_image_and_scalar_test PRIVATE cxx_std_17)
```
below that, we add the test:
```makefile
add_test(NAME add_image_and_scalar_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND add_image_and_scalar_test)
```
finally, we have to pass the test to the `set_tests_properties` function:
```makefile
set_tests_properties(
[...]
add_image_and_scalar_test
[...]
)
```

## Verify that the test is working

Now you can [build the binaries as described in the documentation](https://github.com/clEsperanto/CLIc_prototype/blob/master/docs/guidelines.md#source-compilation).

If the compilation succeeds, you can find your compiled test case in `build/tests/add_image_and_scalar_test`

Execute it and make sure it runs without errors.