所使用编辑器版本:Online_v0.24.0.0
垃圾回收机制(Garbage collection):当需要分配的内存空间不再使用的时候,VM将调用垃圾回收机制来回收内存空间。
(简单理解:当一个对象不再被引用的时候,这个对象占用的内存将会被回收)
GC频率会影响游戏的帧率表现。我们需要通过降低内存申请、内存释放的操作次数,来降低GC频率,从而优化帧率表现。
可以将被“大括号{ }”包裹的代码区域称为一个作用域,这样就能分辨出“最外层”、“最内层”。
1.“内层”作用域能访问“外层”作用域的变量(“外层”不能访问“内层”)
2.在“内层”作用域内申明的变量,如果“外层”不对它进行引用,那么这个变量将会被GC。(因为外部没引用它的话,计算机会认为这个变量只需要在内层作用域使用一次,使用完相当于变成了垃圾,自然就要进行回收了)
举个最常见的例子,在update中改变物体的Location,是不会每帧都去new一个新的Vector的,而是事先声明一个Vector,然后进行重复使用
(不进行复用,每一帧都会创建一个新的Vector,会频繁触发GC)
(进行复用,每帧使用的是最开始创建的Vector)
对象池是游戏开发中最常用的优化手段,当有大量需要重复创建和销毁的对象时,可以使用对象池来进行管理
CPU Cache是位于CPU和内存之间的临时存储器,它的容量比内存小很多但速度极快,可以将内存中的一小部分加载到Cache中,当CPU需要访问这一小部分数据时可以直接从Cache中读取,加快了访问速度。
以一个战斗游戏来举例,战斗中存在大量的需要每帧更新的逻辑(位置、伤害计算、AI、避障计算等等)。如果能在数据排列上能够保证内存的紧邻,利用CPU cache,可以大幅提高帧率数据,降低cache miss导致的不必要的消耗。
尽管脚本代码在实际运行时,会出现乱序存储、vm执行过程中大量读写逻辑以外的数据。这些操作都会破坏cache。而且在业务落地中,很难设计出cache友好类型的数据结构。不过在一些原子逻辑的data-locality模式相比无序创建还是会提高一些运行速度。
(不使用 data-locality 模式下 每个逻辑帧CPU运行的示意图)
(使用 data-locality 模式下 每个逻辑帧CPU运行的示意图)
紧凑模式迭代:按“行”迭代一个数组(一行在数组中是连续的)
松散模式迭代:按“列”迭代一个数组(一列在数组中相差一行距离)
class Boom {
public x: number;
public y: number;
constructor(public id: number) {
}
setPosition(x, y) {
this.x = x;
this.y = y;
}
}
const ROWS = 1000;
const COLS = 1000;
const repeats = 100;
const arr = new Array(ROWS * COLS).fill(0).map((a, i) => new Boom(i));
function localAccess() {
for (let i = 0; i < ROWS; i++) {
for (let j = 0; j < COLS; j++) {
arr[i * ROWS + j].x = 0;
}
}
}
function farAccess() {
for (let i = 0; i < COLS; i++) {
for (let j = 0; j < ROWS; j++) {
arr[j * ROWS + i].x = 0;
}
}
}
function repeat(cb: Function, type: string) {
console.log(`开始${type}迭代`);
const start = Date.now();
for (let i = 0; i < repeats; i++) {
cb();
}
const end = Date.now();
console.log(`完成${type}迭代,耗时:` + ((end - start) / 1000).toFixed(4) + '秒');
return end - start;
}
//repeat(localAccess, "紧凑模式")
repeat(farAccess, "松散模式")
我们游戏战斗中会使用大量的资源(模型,特效,音效),可以建立了一个统一管理所有资源 加载/释放的AssetMgr,并通过打印数据调试,确定每个资源的使用频率,最大缓存个数,缓存命中率等参数,调整各个资源的优先加载权重,预创建数量,缓存个数等参数,降低游戏过程中因创建资源/销毁对象等不必要的开销,来尽可能地达到内存和cpu的平衡。
情况一
有一份资源,只在游戏刚开始时需要的比较多,会达到一个峰值,往后就使用的比较少了。所以可以通过提前计算好资源的实际情况,来进行创建和销毁,而不是让资源一直占用内存。
有一份资源,在游戏中途需要进行大量使用,那么便可以在游戏刚开始的时候就逐步进行创建,而不是等到了要使用的时候再大量创建,这样可以减少卡顿。
资源管理的可操作性比较灵活,需要对自己的游戏进行实际分析后再来执行相应的操作。内存和CPU的使用情况可以使用Devtool来进行分析