0%

有哪些优化指标

项目的优化直接影响着应用的使用体验。首先我们需要选择一些指标来作为优化目标:

  • 运行时指标:帧率FPS是我们最常接触,也是体验最明显的性能指标。一个项目15帧基本上是不能用的状态,30帧勉强能用,能做到60帧则会让用户体验有极大的提高。另一方面,运行时内存大小也十分重要,考虑到移动平台内存条件有限,过大的内存占用很容易造成杀后台和进程崩溃。
  • 离线指标:Unity为了尽可能适配所有类型的资产,提供了丰富繁杂的资产导入设置。然而为了普适性,其中很多默认设置存在性能、存储大小、内存大小等方面的浪费,因此如何优化好一个资产的离线设置也对项目很有帮助。另一方面,减小包体大小对于项目的分发也有所帮助,毕竟没有人愿意动不动就下1G的APP。

下面从离线资产优化开始介绍。

阅读全文 »

参考Game Programming Patterns完成的设计模式总结笔记。

命令模式

命令模式本质上希望把一个硬编码的函数解耦成可配置的函数命令,尽可能将功能触发具体功能分离。

如下有一个玩家对象的输入控制功能,其中按下X键对应jump动作。如果我们想要将X键绑定到fireGun功能,那还得去代码里修改对应的硬编码if语句,然后重新编译,重新链接。

1
2
3
4
5
6
7
void InputHandler::handleInput()
{
if (isPressed(BUTTON_X)) jump();
else if (isPressed(BUTTON_Y)) fireGun();
else if (isPressed(BUTTON_A)) swapWeapon();
else if (isPressed(BUTTON_B)) lurchIneffectively();
}
阅读全文 »

点云空间学习

PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation

  • Qi C R, Su H, Mo K, et al. Pointnet: Deep learning on point sets for 3d classification and segmentation[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2017: 652-660.

PointNet首次基于原始点云进行深度学习,其提出了点云深度学习的三大原则: 无序性、点间联系、变换一致性。基于此, PointNet在点云上逐点运用了MLP进行变换, 并且构造了T-Net进行对抗点云的仿射变换, 最终使用max pool进行对称聚合。

缺少对局部结构的特征学习

PointNet++: Deep Hierarchical Feature Learning on Point Sets in a Metric Space

  • Qi C R, Yi L, Su H, et al. Pointnet++: Deep hierarchical feature learning on point sets in a metric space[J]. Advances in neural information processing systems, 2017, 30.

PointNet没有捕捉到点的局部结构特征,限制了细粒度和复杂场景的识别、泛化能力。PointNet++则引出了一个set abstraction层对点云进行多级学习。set abstraction定义了多级多块的局部邻域结构, 其在每一个局部邻域中都使用了mini-PointNet来进行特征抽取。然而由于点云是非均匀分布的, 不同的局部邻域的密度不一样, 因此PointNet++提出了两种自适应密度的特征融合模块: Multi-scale grouping(MSG)Multi-resolution grouping(MRG)

另外由于部位分割等任务最终需要输出逐点的特征标签, 因此在set abstraction之后, Pointnet++一方面在同一级内进行反距离的插值传播, 另一方面自顶向下进行反向逐级的特征传播。在同一层内对两种传播特征进行拼接, 即得到该层的逐点特征。

阅读全文 »

机制

委托

委托类似于函数指针:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//定义委托类型 / 定义函数指针类型
delegate void MyDelegateType(int a);

//根据类型创建实例对象
MyDelegateType my_delegate_instance=new ();
//注册指向函数
my_delegate_instance+=Func1;
my_delegate_instance+=Func2;
my_delegate_instance+=Func3;
//删除函数
my_delegate_instance-=Func3;
//唤醒函数
my_delegate_instance.Invoke(int_variable);
my_delegate_instance(int_variable);

在我们定义委托类型时,本质上编译器会生成一个继承自标准库的类,如下所示。这个类实际上会形成一个委托的闭包,其会包含调用所需的所有信息,如调用实例、待调用方法的位置。

1
2
3
4
5
6
7
8
9
10
11
12
class MyDelegateType :System.MulticastDelegate
{

//一些继承的重要字段
internal Object _target;//当委托注册了实例方法时,这个字段会填充实例对象,以便调用实例方法。
internal IntPtr _methodPtr;//指针,单播时使用,指向那个注册的方法。
private Object _invocationList;//方法链,多播时使用,指向多个单播委托实例。

//唤醒操作
public virtual void Invoke(int a);
//...其余字段和方法
}

对于单播委托,其会在_methodPtr中直接存储方法地址。然而正如上文所示,一个委托可以注册多个方法。实际上在注册多个方法后,C#会生成一个独立的委托实例,用来指向多个单播委托:

阅读全文 »

概念讨论

内存对齐是一个很基础的概念。虽然内存地址的单位是字节,但是CPU访存指令大部分都是以机器字长(64位的CPU机器字长即8字节)为单位,并且由于内存提供的访存电路接口限制,CPU只能从机器字长的倍数的地址开始访问。即CPU能够在一条指令中访存0000-0007,但是不能够从0003-0010,而必须拆分成两条访存指令,分别从0000和0008开始。

CPU按机器字长访存容易理解,但是只能从倍数地址开始访问难以让人理解。据参考,倍数地址的限制更多来源于内存访问器的硬件连线限制。

在没有内存对齐的情况下,假设有两个连续数据INT8,INT64,分别占据了地址0000和0001-0008。此时64位CPU需要几次访存才能拿到INT64数据呢?

  1. 首先访存0000-0007,拿到0001-0007的部分。
  2. 再次访存0008-0015,拿到0008的部分。

即需要访存两次才能取到数据。因此,通常程序会对数据的布局作出内存对齐,以优化访存效率。例如还是INT8和INT64,对齐后地址分别为0000和0008-0015。这样取INT64就只需要访存一次。

据此,业界给出了一个内存对齐的基本原则:将X字节的数据放在X倍数的起始地址上。这样即可在尽可能节约内存空间的情况下,减少访存指令。

阅读全文 »