02讲: 元编程与面向对象编程

02讲: 元编程与面向对象编程,第1张

元编程与面向对象编程 元编程在taichi中的应用 元编程

Metaprogramming is a programming technique in which computer programs have the ability to treat other programs as their data. It means that a program can be designed to read, generate, analyze or transform other programs, and even modify itself while running.

大体意思就是说,我们可以编写一部分代码(元编程),让这部分代码去产生那部分可以真正运行的代码,然后再让这部分运行的代码,去完成相应的计算,得到结果。

比如说:(仅仅个人理解)

下面这个图,是我们平常程序员所编写的程序,一般来说,我们要解决几个不同的问题,都要去相对应的去编写代码,也就是有几个问题,我们就会编写出几个不同的程序。

而元编程是,面对几个不同的问题,我们先大体设计出代码,也就是绘制出一张图纸,这样再面对不同的问题的时候,就可以只更改图纸上的某个部分,从而解决问题,不再需要去重新编写代码。

应用一(模板复用)

Unify the development of dimensionality-dependent code, such as 2D/3D physical simulations

ti.template()

这个就是taichi的元编程的函数,它具体的用处可以使用下面的例子来进行说明:

@ti.kernel
def copy(src: ti.template(), dst: ti.template(), size: ti.i32):
    for i in range(size):
        dst[i] = src[i]

a = ti.field(ti.f32, 4)
b = ti.field(ti.f32, 4)
c = ti.field(ti.f32, 12)
d = ti.field(ti.f32, 12)

copy(a, b, 4)
copy(c, d, 12)

因为我们知道,taichi是一个强类型的语言,所以,在传入参数时必须指定参数的类型,但是,这个函数,我们要实现拷贝,但是,参数类型那么多,难道我们要写所有类型的拷贝函数嘛?其实是不用的,ti.template()这个代码就解决了这个问题,它将会在代码在执行时,自动判别出类型,并将程序代码放在相应的位置上,这样,我们就不需要写出所有类型的copy函数啦,直接使用带有ti.template()的copy函数就可以了。

注意:

(1)ti.template()函数,仅仅可以识别taichi中定义的参数类型,传入其它类型会进行报错

例如:

(2)ti.template()函数中,也就是taichi scope中无法对python scope中的数据进行修改

例如:

但是,直接对全局变量,也就是带有field的变量进行修改,是可以的。

(3)ti.template()函数,taichi scope中的数据,可以被taichi scope中 *** 作修改

我现在感觉,kernel也好,func也好,都是类似于C++环境,也就是说,这个ti.template()函数是传入的类似于c++中的引用(&),从而可以在taichi scope中进行修改。但是,python scope(也就是python环境)中的数据无法被taichi scope修改。

明白上述规则之后,就可以写出适应于taichi所有数据类型进行copy的函数了。


@ti.kernel
def copy(src: ti.template(), dst: ti.template()):
    for i in src:
        dst[i] = src[i]

a = ti.field(ti.f32, 4)
b = ti.field(ti.f32, 4)
c = ti.field(ti.f32, 12)
d = ti.field(ti.f32, 12)

copy(a, b)
copy(c, d)

但是,这样仅仅支持一维的field,如果我们想要copy多维的field呢,难不成像这样一样?

import taichi as ti
ti.init(arch=ti.cpu)
@ti.kernel
def copy_1D(x: ti.template(), y: ti.template()):
    for i in x:
        y[i] = x[i]
@ti.kernel
def copy_2d(x: ti.template(), y: ti.template()):
    for i, j in x:
        y[i, j] = x[i, j]
@ti.kernel
def copy_3d(x: ti.template(), y: ti.template()):
    for i, j, k in x:
        y[i, j, k] = x[i, j, k]

这样就有了下面的函数:ti.grouped()

ti.grouped()

继续上面的问题,当我们使用了ti.grouped()函数之后,就可以写成:

@ti.kernel
def copy(x: ti.template(), y: ti.template()):
    for I in ti.grouped(y):
        # I is a vector with dimensionality same to y
        # If y is 0D, then I = ti.Vector([]), which is equivalent to `None` used in x[I]
        # If y is 1D, then I = ti.Vector([i])
        # If y is 2D, then I = ti.Vector([i, j])
        # If y is 3D, then I = ti.Vector([i, j, k])
        # ...
        x[I] = y[I]

是不是就很方便了,遍历i,j,k····都需要I就可以了,这样就可以遍历所有的field中的元素了。

应用二:加快taichi代码 ti.static()

(1)强行将循环展开

也就是在kernel中的for循环将不再并行化,而是直接展开。
例如这两个程序:

from numpy import intp
import taichi as ti

ti.init(arch=ti.gpu)

@ti.kernel # 并行
def foo(x: ti.template()):
    for i in range(x):
        print(i)

x = int(input("Enter x: "))

foo(x)

from numpy import intp
import taichi as ti

ti.init(arch=ti.gpu)

@ti.kernel
def foo(x: ti.template()):
    for i in ti.static(range(x)): #强行将循环展开
        print(i)

x = int(input("Enter x: "))

foo(x)

面向对象编程

面向对象:

就是python中定义类和对象等相关的方法

taichi当中的类:@ti.data_oriented

taichi当中的类就是利用python中的类的定义方法以及使用方法,只不过需要在类前面加上 @ti.data_oriented

比如:

@ti.data_oriented
class TaichiWheel:
    def __init__(self, radius, width, rolling_fric):
        self.radius = radius
        self.width = width
        self.rolling_fric = rolling_fric
        self.pos = ti.Vector.field(3, ti.f32, shape=4)
    @ti.kernel
    def Roll(self):
        ...
    @ti.func
    def foo(self):
        ...

那么加上这个@ti.data_oriented之后,就可以定义一个taichi的类了,具体的用处是:

  • 可以在taichi的类中定义taichi语言所支持的数据类型
  • 也可以在taichi类中利用@kernel定义并行计算的函数来提高性能了

比如:

@ti.data_oriented
class CelestialObject:
    def __init__(self):
        self.pos = ti.Vector(2, ti.f32, shape = N)
        self.vel = ti.Vector(2, ti.f32, shape = N)
        self.force = ti.Vector(2, ti.f32, shape = N)
@ti.data_oriented
class CelestialObject:
    ...
    @ti.kernel
    def update(self, h: ti.f32):
        for i in self.vel:
            self.vel[i] += h * self.force[i] / self.Mass()
            self.pos[i] += h * self.vel[i]

当然,在taichi中也支持了三大件:封装、继承、多态

封装

封装:就是把类的成员变量和成员函数都封装起来,使得外部无法访问到类的成员变量和成员函数

继承

继承:就是把一个类的成员变量和成员函数继承到另一个类中,使得另一个类可以访问到父类的成员变量和成员函数

多态

就是虽然外部调用的函数名相同,但是属于不同类的函数,其实现的方式可能是不同

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/915170.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存