# Release Note
`0.0.1` 初始版本,划分了文件结构
`0.0.2` 利用RefCell和Rc来解决点引用的问题。
`0.0.3` 发现trait的结构不正确,应该为:Map是一个trait,AStart或者Recast来实现它。
`0.0.4` PartialEq 这个trait原来也可以用来比较两个不同类型之间的比较,但是A == B 和 B == A需要实现两遍
`0.0.5` 2023.12月25日:尝试tokio以 多线程+await 的模式来运行,MapManager是一个单例,new_astar需要写锁,但是find_path只需要读锁。
当中试图用到了Rust 1.75的新特性,也就是:trait中可以有async方法,这个是个很重要的修改。
Rust 1.75 两个重大的修改就是:
1:trait支持async方法
2:返回类型支持 impl Trait
Rust团队承诺2023.12.28发布1.75 [Rust 1.75.0](https://releases.rs/docs/1.75.0/)
因为tokio的mutex.lock是await的,所以有些方法的签名必须是async,
等1.75发布就修改trait Map的find_path为async
`0.0.6` 优化一些不必要的锁,和一些返回类型
`0.0.7` 优化MapManager单例获取的方式
`0.0.8` 优化IdGenerator单例的获取方式,简化id生成的规则:(64位时间戳+64位序号)
`0.0.9` 实现A*算法,但是比较粗糙,16 * 16格子,从(1,0)寻到(14,15),循环1000次,用时100ms左右
`0.0.10` 重大优化,将openList中存储点的数据结构改为BTreeMap<i64, Vec < PointType > >
其中key代表A*算法中的F,这样快速取出F最小的点只要first_entry就可以,然后从Vec中pop最后一个即可。
查询一个点在BTreeMap是否存在,通过F取出vec后,循环判断。如果要删除就从vec中删除。
example中:
使用256*256格子,从(1,0)寻到(254,255),单次,Debug 50ms左右,Release 33ms左右
按理说,1000次应该在33000左右,再除以内核数16,应该在2000ms(预估)左右,因为Intel的超线程技术
16和有24个逻辑线程,最终执行结果为下面的1150ms左右。所以超线程技术还是有用的。提升极大。
优化前:
16*16格子,从(1,0)寻到(14,15),循环1000次,用时100ms左右
优化后:
16*16格子, 从(1,0)寻到(14,15),循环1000次,用时(20ms)左右.
32*32格子, 从(1,0)寻到(30,31),循环1000次,用时(50ms)左右.
64*64格子, 从(1,0)寻到(63,63),循环1000次,用时(120ms)左右.
128*128格子, 从(1,0)寻到(126,127),循环1000次,用时(330ms)左右.
256*256格子, 从(1,0)寻到(254,255),循环1000次,用时(1100ms)左右.
开发机:
CPU:
13th Gen Intel(R) Core(TM) i7-13700F
内核: 16 (8小核+8大核)
逻辑处理器: 24(只有8个大核有超线程技术,所以是24逻辑核)
内存:
64GB DDR4 4800 MHz
寻路使用tokio异步,默认线程数。
`0.0.14` 又做了一些优化
1:将点坐标类型改从i64 -> i32。
2:start和end之哦姐改为原始的Point类型,省去了borrow过程。
3:把contain_point再写一个contain_point_type,用来检测Point或陪着PointType
4:vec增加一些初始大小,减少vec扩容操作数量。
优化后:
16*16格子, 从(1,0)寻到(14,15),循环1000次,用时(15ms)左右.
32*32格子, 从(1,0)寻到(30,31),循环1000次,用时(40ms)左右.
64*64格子, 从(1,0)寻到(63,63),循环1000次,用时(120ms)左右.
128*128格子, 从(1,0)寻到(126,127),循环1000次,用时(330ms)左右.
256*256格子, 从(1,0)寻到(254,255),循环1000次,用时(1015ms)左右.
数据越大,提升越明显(应该是vec初始容量的结果,但是转到i32似乎也有提升,很奇怪)
`0.0.15` 增加从文件读取二进制地图的方式
`0.0.16` 继续优化,改造算法
1:去掉了不必要的if
2:closeList用简单的二维数组实现,查询更快
3:g,f 用二维数组存储
4:使用了网上更准确的算法
优化后:
16*16格子, 从(1,0)寻到(14,15),循环1000次,用时(15ms)左右.
32*32格子, 从(1,0)寻到(30,31),循环1000次,用时(30ms)左右.
64*64格子, 从(1,0)寻到(63,63),循环1000次,用时(80ms)左右.
128*128格子, 从(1,0)寻到(126,127),循环1000次,用时(160ms)左右.
256*256格子, 从(1,0)寻到(254,255),循环1000次,用时(330ms)左右.
性能已经到了可用的程度。
`0.0.17` 调整坐标系,之前x和y弄反了从文件或者输出话时,内层是x,外层是y
`0.0.18` col和row再load的时候确定,运行时不在使用map.len和map[0].len。
去掉一些不必要的borrow和Rc<RefCell<>>,寻路的性能优化暂时停止,接下来开发外围系统。
比如:用gRPC远程调用,从json文件读取地图,是否增加障碍物动态改变,等等
优化后:
16*16格子, 从(1,0)寻到(14,15),循环1000次,用时(10ms)左右.
32*32格子, 从(1,0)寻到(30,31),循环1000次,用时(30ms)左右.
64*64格子, 从(1,0)寻到(63,63),循环1000次,用时(60ms)左右.
128*128格子, 从(1,0)寻到(126,127),循环1000次,用时(130ms)左右.
256*256格子, 从(1,0)寻到(254,255),循环1000次,用时(270ms)左右.
`0.0.19`
1:Rust改为1.75。
`0.0.20`
1:为了学习,增加了一个Point类型的内存池,作为features,默认不启用。(没弄好,性能会下降)
2:用std::time::Instant来统计代码执行时间。
我现在发现,在Rust中,以前的优化思想,比如:对象池。在A*寻路这种算法中,起不到大的作用。
就连预分配vec容量,能得到的性能改进都非常小。
也许是我水平有限、也许是Rust本身优化的很到位。我估计,只有在图形引擎这种应用中,对象池才比较有用。
`0.0.21`
1:这次并没有修改A*算法本身,只是将find_path接口的RWLock修改为了使用标准库的,这样做的原因,个人认为对于A*寻路算法,获取锁的时候进行异步wait并没有什么意义
2:修改了RWLock后,我在test中就改为了使用rayon库在进行多线程并发测试,结果入下:
修改后:
16*16格子, 从(1,0)寻到(14,15),循环1000次,用时(60ms)左右.
32*32格子, 从(1,0)寻到(30,31),循环1000次,用时(60ms)左右.
64*64格子, 从(1,0)寻到(63,63),循环1000次,用时(60ms)左右.
128*128格子, 从(1,0)寻到(126,127),循环1000次,用时(60ms)左右.
256*256格子, 从(1,0)寻到(254,255),循环1000次,用时(60ms)左右.
刚开始我以为代码写错了,导致了根本没有等待返回就直接出了结果,但是经过打印运行结果,发现是正确的,并没有问题
所以,我认为在Rust中,针对具体负载选择正确的并发库非常重要,“IO等待多,用tokio。CPU型多任务,用Rayon”
`0.1.2`
目前已经可以使用了:
1:导出的C接口,可以使用C语言调用。具体的例子在a.c b.c c.c三个文件中。
2:在x64的windows,linux上。aarch64安卓上都可以顺利运行。(使用Cross交叉编译)
3:load_map_from_string是从json中读取地图占位格,具体内容见World.json
4:load_map暂时不要使用,安卓中这个文件路径还没确定。
使用 cross build --release --target x86_64-unknown-linux-gnu 编译linux版本
使用 cross build --release --target aarch64-linux-android 编译andriod版本
使用 cross build --release --target x86_64-pc-windows-msvc 编译andriod版本
cbindgen --crate game_pathfinding --output pathfinding.h 生成头文件
同时生成C语言的静态库和动态库:
[lib]
name = "pathfinding"
crate-type = ["staticlib", "dylib"]
linux下链接动态库:(写代码时用dlopen加载动态库文件)
gcc -o a a.c -ldl -rdynamic
linux下链接静态库:(写代码是静态库,运行时不连接)
gcc -o b b.c -L. libpathfinding.a -lpthread -lm -ldl
linux下链接动态库,但是不用dlopen模式:系统会在$LD_LIBRARY_PATH中寻找libpathfinding.so。(写代码像静态库,运行时找动态库)
gcc -o c c.c -L. -lpathfinding -lpthread -lm -ldl
a.c,b.c,c.c的例子在examples中
`0.1.3`
为了解决 Clippy 的规则,将锁替换为tokio::sync::RwLock