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

[doc] Refactored ODOP #6143

Merged
merged 3 commits into from
Sep 23, 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
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
---
sidebar_position: 2
sidebar_position: 3
---

# Objective Data-oriented Programming I
# Data-oriented Class

Taichi is a [data-oriented](https://en.wikipedia.org/wiki/Data-oriented_design) programming (DOP) language. However, simple DOP makes modularization hard. To allow modularized code, Taichi borrows some concepts from object-oriented programming (OOP). For convenience, let's call the hybrid scheme **objective data-oriented programming** (ODOP).
To define a Taichi kernel as a Python class member function:

The ODOP scheme allows you to organize data and methods into a class and call the methods to manipulate the data in the Taichi scope. Taichi offers two different types of classes that serve this purpose, and they are distinguished by the two decorators `@ti.data_oriented` and `@ti.dataclass`, respectively:

1. `@ti.data_oriented`: It should be used when your data is actively updated in the Python scope (such as current time and user input events) and tracked in Taichi kernels. This type of class can have native Python objects as members and must be instantiated in the Python scope. This article will discuss this type of class in full detail.

2. `@ti.dataclass`: It is a wrapper over `ti.types.struct` but offers more flexibility: You can define Taichi functions as its methods and invoke these methods in the Taichi scope. We will discuss this type of class in the next article.


## Data-oriented classes

### Introduction

If you need to define a **Taichi kernel** as a Python class member function, please decorate the class with a `@ti.data_oriented` decorator. You can then define `ti.kernel`s and `ti.func`s in your *data-oriented* Python class.
1. Decorate the class with a `@ti.data_oriented` decorator.
2. Define `ti.kernel`s and `ti.func`s in your data-oriented Python class.

:::note
The first argument of the function should be the class instance ("`self`"), unless you are defining a `@staticmethod`.
Expand Down Expand Up @@ -62,7 +52,7 @@ class MyClass:


a = MyClass()
# a.call_inc() cannot be called, since a.temp has not been allocated at this point
# a.call_inc() cannot be called, because a.temp has not been allocated at this point
a.allocate_temp(4)
a.call_inc()
a.call_inc()
Expand Down Expand Up @@ -109,9 +99,9 @@ print(a.y) # [ 5. 13. 21. 29.]
```


### Inheritance of data-oriented classes
## Inheritance of data-oriented classes

The *data-oriented* property will be automatically carried beyond the Python class inheriting. This means the **Taichi Kernel** could be called while any of the ancestor classes are decorated by the `@ti.data_oriented` decorator.
The data-oriented property is automatically carried along with the Python class inheriting. This means that you can call a Taichi Kernel if any of its ancestor classes is decorated with `@ti.data_oriented`.

An example:
```python
Expand Down Expand Up @@ -161,18 +151,18 @@ print(b.count()) # 1
c = BaseClass()
# c.add(3)
# print(c.count())
# The two lines above will trigger a kernel define error, since class c is not decorated by @ti.data_oriented
# The two lines above trigger a kernel define error, because class c is not decorated with @ti.data_oriented
```

### Python built-in decorators
## Python built-in decorators

Common decorators that are pre-built in Python, `@staticmethod`[^1] and `@classmethod`[^2], could decorate to a **Taichi kernel** in *data-oriented* classes.
Common decorators that are pre-built in Python, `@staticmethod`[^1] and `@classmethod`[^2], can decorate a Taichi kernel in data-oriented classes.

[^1]: [Python built-in functions - staticmethod](https://docs.python.org/3/library/functions.html#staticmethod)
[^2]: [Python built-in functions - classmethod](https://docs.python.org/3/library/functions.html#classmethod)


`staticmethod` example :
`staticmethod` example:

```python {16}
import taichi as ti
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
---
sidebar_position: 3
sidebar_position: 4
---

# Objective Data-oriented Programming II


## Taichi dataclasses
# Taichi Dataclass

Taichi provides custom [struct types](../type_system/type.md#compound-types) for developers to assemble pieces of data together. However, it would be more convenient to have:
1. A Python representation of the struct type which is more object oriented.
2. Functions associated with a struct type (C++-style structs).

- A Python representation of the struct type which is more object oriented.
- Functions associated with a struct type (C++-style structs).


To achieve the ends, Taichi enabled the `@ti.dataclass` decorator on a Python class. This is inspired by Python's [dataclass](https://docs.python.org/3/library/dataclasses.html) feature, which uses class fields with annotations to create data types.

### Creating a struct from a Python class
Here is an example of how we could define a Taichi struct type under a Python class:
## Create a struct from a Python class

The following is an example of defining a Taichi struct type under a Python class:

```python
@ti.dataclass
class Sphere:
center: vec3
radius: ti.f32
```
This will create the *exact* same type as using `ti.types.struct()`:
This creates the *exact* same type as using `ti.types.struct()`:

```python
Sphere = ti.types.struct(center=vec3, radius=ti.f32)
Expand All @@ -34,7 +33,7 @@ The `@ti.dataclass` decorator converts the annotated members in the Python class
sphere_field = Sphere.field(shape=(n,))
```

### Associating functions with the struct type
## Associate functions with the struct type
Python classes can have functions attached to them, and so can Taichi struct types. Building from the above example, one can embed functions in the struct as follows:

```python
Expand Down Expand Up @@ -68,7 +67,8 @@ def get_area() -> ti.f32:
get_area() # 201.062...
```

### Notes
## Notes

- Inheritance of Taichi dataclasses is not supported.
- While it is convenient and recommended to associate functions with a struct defined via `@ti.dataclass`, `ti.types.struct` can serve the same purpose with the help of the `__struct_methods` argument. As mentioned above, the two methods of defining a struct type produce identical output.

Expand Down
12 changes: 12 additions & 0 deletions docs/lang/articles/advanced/odop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
sidebar_position: 2
---

# Objective Data-oriented Programming

Taichi is a [data-oriented](https://en.wikipedia.org/wiki/Data-oriented_design) programming (DOP) language. However, one-size-fits-all DOP makes modularization hard. To allow modularized code, Taichi borrows some concepts from object-oriented programming (OOP). For convenience, let's call the hybrid scheme objective data-oriented programming (ODOP).

The ODOP scheme allows you to organize data and methods in a class and call the methods to manipulate the data in the Taichi scope. Taichi offers two different types of classes that serve this purpose, and they are distinguished by the two decorators `@ti.data_oriented` and `@ti.dataclass` respectively:

- Decorated with `@ti.data_oriented`, a data-oriented class is used when your data is actively updated in the Python scope (such as current time and user input events) and tracked in Taichi kernels. This type of class can have native Python objects as members and must be instantiated in the Python scope. [Data-oriented Class](./data_oriented_class.md) describes this type of class.
- Decorated with `@ti.dataclass`, a dataclass is a wrapper of `ti.types.struct`. A dataclass provides more flexibilities. You can define Taichi functions as its methods and call these methods in the Taichi scope. [Data Class](./dataclass.md) describes this type of class.
2 changes: 1 addition & 1 deletion docs/lang/articles/advanced/quant.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 4
sidebar_position: 5
---

# Use quantized data types
Expand Down
10 changes: 5 additions & 5 deletions docs/lang/articles/get-started/accelerate_pytorch.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ Researchers in machine learning usually spend a lot of time designing model arch

[This repo](https://github.com/BlinkDL/RWKV-CUDA) introduces an example of customizing an ML operator in CUDA. The author developed an RWKV language model using sort of a one-dimensional depthwise convolution custom operator. The model does not involve much computation but still runs slow because PyTorch does not have native support for it. So, the author customized the operator in CUDA using a set of optimization techniques, such as loop fusion and Shared Memory, and achieved a performance 20x better than he did with PyTorch.

Referring to the CUDA code[3], we customized a Taichi depthwise convolution operator[4] in the RWKV model using the same optimization techniques.
Referring to the CUDA code[^3], we customized a Taichi depthwise convolution operator[^4] in the RWKV model using the same optimization techniques.

The function of the depth wise convolution operator:

Expand Down Expand Up @@ -217,7 +217,7 @@ All these features set Taichi apart as a convenient tool for ML operator customi

## Reference

- 1 [Pure PyTorch padding](https://github.com/ailzhang/blog_code/blob/master/tile/demo_torch.py)
- 2 [Padding PyTorch tensor in Taichi kernel](https://github.com/ailzhang/blog_code/blob/master/tile/demo_taichi.py)
- 3 [RWKV-CUDA](https://github.com/BlinkDL/RWKV-CUDA/tree/main/depthwise_conv1d)
- 4 [RWKV-Taichi](https://github.com/ailzhang/blog_code/tree/master/rwkv)
[^1] [Pure PyTorch padding](https://github.com/ailzhang/blog_code/blob/master/tile/demo_torch.py)
[^2] [Padding PyTorch tensor in Taichi kernel](https://github.com/ailzhang/blog_code/blob/master/tile/demo_taichi.py)
[^3] [RWKV-CUDA](https://github.com/BlinkDL/RWKV-CUDA/tree/main/depthwise_conv1d)
[^4] [RWKV-Taichi ](https://github.com/ailzhang/blog_code/tree/master/rwkv)
4 changes: 2 additions & 2 deletions docs/lang/articles/get-started/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pip install taichi
```
You can also build Taichi from source: See our [developer's guide](../contribution/dev_install.md) for full details. We *do not* advise you to do so if you are a first-time user, unless you want to experience the most up-to-date features.

To verify a successful installation, run the following command in the terminal:
To verify a successful installation, run the following command in your terminal:

```bash
ti gallery
Expand Down Expand Up @@ -158,7 +158,7 @@ Taichi offers a handy syntax sugar: It parallelizes any `for` loop at the outerm

Note that the field `pixels` is treated as an iterator. As the indices of the field elements, `i` and `j` are integers falling in the ranges `[0, 2*n-1]` and `[0, n-1]`, respectively. They are arranged in the row-majored order, i.e., `(0, 0)`, `(0, 1)`, ..., `(0, n-1)`, `(1, n-1)`, ..., `(2*n-1, n-1)`.

You should keep it in mind that the *for loops not at the outermost scope will not be parallelized*; they are handled serially:
Keep in mind that the *for loops not at the outermost scope will not be parallelized*; they are handled serially:

```python {3,7,14-15}
@ti.kernel
Expand Down
2 changes: 1 addition & 1 deletion docs/lang/articles/type_system/type.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ The code above serves the same purpose as the line below does but provides bette
Sphere = ti.types.struct(center=vec3, radius=float)
```

Another advantage of using `@ti.dataclass` over `ti.types.struct` is that you can define member functions in a dataclass and call them in the Taichi scope, making object-oriented programming (OOP) possible. See the article [objective data-oriented programming](../advanced/odop2.md) for more details.
Another advantage of using `@ti.dataclass` over `ti.types.struct` is that you can define member functions in a dataclass and call them in the Taichi scope, making object-oriented programming (OOP) possible. See the [objective data-oriented programming](../advanced/odop.md) for more information.


### Initialization
Expand Down