最近做的优化比较多,整理下和Go内存相关的一些东西。

一. 不要过早优化

虽是老生常谈,但确实需要在做性能优化的时候铭记在心,个人的体会:

  1. first make it work, then measure, then optimize
  2. 二八原则
  3. 需求变更快
  4. 对性能的主观直觉不靠谱
阅读全文 »

简单谈谈我们最近是如何给GS做压测的。

1. 压测机器人

压测机器人需要满足如下几个条件:

  1. 异步请求: 异步才能模拟真实的客户端请求和压力
  2. 数据同步: 像客户端一样缓存和处理服务器响应数据,这样才能做好有效请求和可重入
  3. 可重入: 机器人应该可以在任何时候关闭/重启,而不应该假设初始状态(比如只有注册的时候能跑)
  4. 随机性: 机器人行为尽可能随机分布,并且每次重启重新初始化随机种子
阅读全文 »

这段时间学习OOP对语言和编程范式有一些新的理解,之前系统整理过函数式编程,因此先从OOP谈起。我们先回顾下面向对象(OOP)的核心思想:

  1. 将数据及其相关操作(方法)封装起来,以对象的方式暴露出来,对象与对象之间通过方法调用(或者说是发消息)进行通信。
  2. 对象可以有自己的私有字段,只有对象的方法可以访问这些字段。
  3. 每个对象都是一个类(Class)的实例,类定义了对象的行为(内部数据和方法实现)。

与函数式的”一切皆函数”一样,OOP也有一个宏大的目标”一切皆对象”。

阅读全文 »

本文主要谈谈CPU Cache的设计,内存屏障的原理和用法,最后简单聊聊内存一致性。

我们都知道存储器是分层级的,从CPU寄存器到硬盘,越靠近CPU的存储器越小越快,离CPU越远的存储器越大但越慢,即所谓存储器层级(Memory Hierarchy)。以下是计算机内各种存储器的容量和访问速度的典型值。

存储器类型 容量 特性 速度
CPU寄存器 几十到几百Bytes 数据电路触发器,断电丢失数据 一纳秒甚至更低
Cache 分不同层级,几十KB到几MB SRAM,断电丢失数据 几纳秒到几十纳秒
内存 几百M到几十G DRAM,断电丢失数据 几百纳秒
固态硬盘(SDD) 几十到几百G SSD,断电不丢失数据 几十微秒
机械硬盘(HDD) 上百G 磁性介质和磁头,断电不丢失数据 几毫秒

从广义的概念上来说,所有的存储器都是其下一级存储器的Cache,CPU Cache缓存的是内存数据,内存缓存的是硬盘数据,而硬盘缓存的则是网络中的数据。本文只谈CPU Cache,一个简单的CPU Cache示意图如下:

图中忽略了一些细节,现代的CPU Cache通常分为三层,分别叫L1,L2,L3 Cache, 其中L1,L2 Cache为每个CPU核特有,L3为所有CPU核共有,L1还分为缓存指令的i-cache(只读)和缓存程序数据的d-cache,L2 L3 Cache则不区分指令和程序数据,称为统一缓存(unified cache)。本文主要讨论缓存命中和缓存一致性的问题,因此我们只关注L1 Cache,不区分指令缓存和程序数据缓存。

阅读全文 »

Perf(Performance Event)是Linux 2.6.31后内置的性能分析工具,它相较其它Prof工具最大的优势在于与Linux Kernel紧密结合,可以进行内核甚至硬件级的性能分析。我之前只零散地用一些ptrace,strace之类的小工具,与Perf比起来,确实小巫见大巫。也赶紧花了点时间简单了解和试用一下,添加到工具箱,以备不时之需。

阅读全文 »

游戏服务器难吗?不就是处理和保存一些玩家数据么?本文谈谈我对这个问题的一些理解,或者是说,如果说游戏服务器开发很难,那么它究竟难在哪里?

一. 状态性

游戏服务器是后端,做后端的,每天耳濡目染横向扩展,自动伸缩等炫酷的特性,要说放在以前,这些特性还是巨头的”专利”,我们想要自己实现这些东西挑战性是比较大的,但近几年有了容器生态如k8s的加持,只要你实现了一个无状态应用,你几乎马上就可以得到一个可伸缩的集群,享受无状态本身带来的各种好处,机器挂了自动重启,性能不够了就自动扩展等等。而作为一名游戏服务器开发者,自然也想充分享受容器时代的红利,所以我们来捋捋无状态游戏服务器的可行性。

阅读全文 »

本文谈谈在函数式编程中的延迟计算,惰性求值等技术及其应用。

Racket

本文将以 Racket 为例,以下是 Racket 的概要:

  1. 更广泛的函数概念: 什么 if define + myfunc 等,统统都是函数
  2. 前缀表达式: 同 Lisp, Scheme 等语言一样,Racket 使用前缀表达式,如(myfunc 1 2),(+ 1 2 3)等
  3. 支持可变性: 可修改变量值(不建议),如 (set! x 2),并且支持修改 Pair,Map 的指定元素

其它关于 Racket 的具体语法和 API 细节请参考Raccket 中文文档

Delay Evaluation

Delay Evaluation 意为延迟计算,即表达式只在必要时才求值,而非被赋给某个变量时立即求值。

阅读全文 »

本文简单讨论如何在服务端实现基于三角形网格的寻路算法。

地图表示

1. 基于路点

基于路点是最原始的寻路方案,比如要让你实现一个迷宫,你会很自然地想到用0代表通路,1代码障碍物,整个迷宫地图由0和1构成的点阵来表示。这就是基于路点,本质上是将地图上可以通过的位置(坐标状态)都记录下来。

阅读全文 »

前段时间又和同事讨论到 GS 中的 ACID 一致性,在这里唠叨几句。ACID 一致性即系统内部的数据一致性,而非分布式系统对外体现的一致性(CAP 中的 C)。

假设我们有一个业务逻辑叫做行军,在单线程下,其逻辑如下:

阅读全文 »

调度的概念这里就不赘述了,调度本质上就是一个资源分配算法,本文谈谈调度的基础策略,常见模型,以及 Go 和 Erlang 的一些调度特性。

调度策略

抢占 vs 协作

从调度机制上来讲,调度可以分为抢占式和协作式。

非抢占方式是指一旦将调度资源(如 CPU)分配给某任务 后,便让该任务一直执行,直到该任务完成或阻塞或主动让出CPU控制权,非抢占调度又称为协作式调度,它实现简单,并且对共享资源的访问也更安全(如允许使用不可重入函数)。非抢占算法常见的如 FIFO,STCF(Short time to complete first)等。

阅读全文 »