BruceChen7 / gitblog

My blog
6 stars 1 forks source link

DOP理解 #61

Open BruceChen7 opened 1 year ago

BruceChen7 commented 1 year ago

参考资料

(usage:: 什么是 data oriented design )

data oriented programming vs data oriented design

什么是 ECS

ECS 数据模型的强大之处

ecs 是弱关系模型

关系模型实现数据模型

CREATE TABLE Entity (
    id SERIAL PRIMARY KEY
);

CREATE TABLE Position (
    entity_id INT PRIMARY KEY,
    x FLOAT,
    y FLOAT,
    z FLOAT,
    FOREIGN KEY (entity_id) REFERENCES Entity(id)
);

CREATE TABLE Velocity (
    entity_id INT PRIMARY KEY,
    x FLOAT,
    y FLOAT,
    z FLOAT,
    FOREIGN KEY (entity_id) REFERENCES Entity(id)
);

CREATE TABLE Player (
    entity_id INT PRIMARY KEY,
    username TEXT,
    FOREIGN KEY (entity_id) REFERENCES Entity(id)
);

CREATE TABLE Enemy (
    entity_id INT PRIMARY KEY,
    target_entity_id INT,
    FOREIGN KEY (entity_id) REFERENCES Entity(id),
    FOREIGN KEY (target_entity_id) REFERENCES Entity(id)
);

为什么采用 ECS 这种弱关系模型而不是关系模型

作者的观点

OOP 已死

如何处理

对于任何有重要数据量的数据,设计都会类似于数据库。

basic data operation 基本原则

statement managment

basic concurrent control

(usage:: zig 作者 dop 观点 )

memory layout

use indexes instead of pointers

// x86-64 为 16
const Monster = struct {
    anim: *Animation,
    hp: u32,
    y: u32,
};
var alive_monster: ArrayList(Monster) = .{};
var dead_monster: ArrayList(Monster) = .{};
//  为 12 字节
const Monster = struct {
    anim: u32,
    hp: u32,
    y: u32,
}

store boolean out-of-band

const Monster = struct {
    anim:  *Animation,
    hp: u32,
    y: u32,
    is_alive: bool // 浪费
};
// 大量的浪费
var alive_monster: ArrayList(Monster) = .{}

// 换成
var alive_monster: ArrayList(Monster) = {};
var dead_monster: ArrayList(Monster) = {};

eliminate padding with struct of arrays

const Monster = struct {
    anim: *Animation,
    kind: Kind,
    const Kind = enum {
        snake, bat, wolf, human
    };
};
var monster: ArrayList(Monster) = .{};

Store sparse data in hash map

const Monster = struct {
    hp:  u32,
    x: u32;
    y: u32,
    held_items: [4]u32,
};
var monster: ArrayList(Monster) = .{}

encoding approaches

const Monster = struct {
    tag:  Tag,
    common: Common,
    const Tag = enum {
        bee_yellow,
        bee_black,
        bee_red,
        human_naked
    };
    const Common = struct {
        x: u32,
        y: u32,
        extra_index: u32, // 指向了下面的 index
    };
    const HumanClothed = struct {
        hat: u32,
        shoes: u32,
        shirt: u32,
        pants: u32,
    };

    // 有 extra_index 指定
    var clothed_human: ArrayList(Monster.HumanClothed) = .{};
}

handlers are the better pointers

要点

Move memory management into central systems

Group items of the same type into arrays

额外的优点是

public index-handles 而不是指针

好处

优点

内存安全考虑

优化的地方

存在性处理

复杂度

为什么要使用 if

处理类型

不要使用 bool 值

一个例子
查看流控制语句来改善这一点

不要经常使用枚举

多态的实现

运行时多态

例子
// OOP
include <iostream>
#include <memory>

// 基类
class Shape {
public:
    virtual ~Shape () {}
    virtual double area () const = 0; // 纯虚函数,定义接口
};

// 派生类:圆形
class Circle : public Shape {
private:
    double radius;
public:
    Circle (double r) : radius (r) {}
    double area () const override { // 重写基类的 area 函数
        return 3.14159 * radius * radius;
    }
};

// 派生类:矩形
class Rectangle : public Shape {
private:
    double width;
    double height;
public:
    Rectangle (double w, double h) : width (w), height (h) {}
    double area () const override { // 重写基类的 area 函数
        return width * height;
    }
};

// 函数,接受 Shape 类型的指针,并调用 area 函数
void printArea (const Shape* shape) {
    std::cout << "Area:" << shape->area () << std::endl;
}

int main () {
    std::unique_ptr<Shape> circle = std::make_unique<Circle>(5.0);
    std::unique_ptr<Shape> rectangle = std::make_unique<Rectangle>(4.0, 6.0);

    printArea (circle.get ()); // 输出圆形的面积
    printArea (rectangle.get ()); // 输出矩形的面积

    return 0;
}

// DOP
#include <iostream>
#include <vector>
#include <functional>

// 定义一个结构体来表示形状的属性
struct ShapeAttributes {
    std::string name;
    double value1;
    double value2;
};

// 定义一个函数来计算圆形的面积
double calculateCircleArea (const ShapeAttributes& attributes) {
    double radius = attributes.value1;
    return 3.14159 * radius * radius;
}

// 定义一个函数来计算矩形的面积
double calculateRectangleArea (const ShapeAttributes& attributes) {
    double width = attributes.value1;
    double height = attributes.value2;
    return width * height;
}

// 定义一个函数指针类型,用于存储不同形状的面积计算函数
using AreaCalculator = double (*)(const ShapeAttributes&);

// 定义一个表格,将形状类型和对应的面积计算函数关联起来
std::vector<std::pair<std::string, AreaCalculator>> shapeCalculators = {
    {"Circle", calculateCircleArea},
    {"Rectangle", calculateRectangleArea}
};

// 函数,根据形状的类型调用相应的面积计算函数
void printArea (const ShapeAttributes& attributes) {
    for (const auto& pair : shapeCalculators) {
        if (pair.first == attributes.name) {
            std::cout << "Area:" << pair.second (attributes) << std::endl;
            break;
        }
    }
}

int main () {
    ShapeAttributes circleAttributes = {"Circle", 5.0, 0.0};
    ShapeAttributes rectangleAttributes = {"Rectangle", 4.0, 6.0};

    printArea (circleAttributes); // 输出圆形的面积
    printArea (rectangleAttributes); // 输出矩形的面积

    return 0;
}

事件处理

componet based objects

there is no entity

数据和意义的分离

搜索

索引

data-oriented lookup

例子
考虑的角度

性能

耗时最多的地方

什么时候开始优化

type/system #keyword/dop #public