Main

September 22, 2023

一个任务调度算法引起的性能问题

这两天遇到一个任务调度算法引起的性能问题,花了颇多精力排查和解决。问题出在我写的 ltask 这个 lua 多任务库上。ltask 最初是对 skynet 的一些反思中开始的,最初只是想换一种思路实现 skynet :做一个库而不是框架、更少的锁竞争、避免服务因为消息队列堆积而过载……

后来、我们游戏引擎开始尝试基于 ltask 利用手机设备上的多核,渐渐的便完善起来,也发展出和 skynet 不同的部分。它最近两年一直是围绕移动设备客户端程序优化,所以网络部分并非重点,也就不需要像 skynet 那样把网络模块做在框架底层,而是以一个独立服务存在。而网络 IO 、文件 IO 、客户端窗口这些部分又不适合于其它渲染相关的服务混在一起,因为它们需要和操作系统直接打交道,所以我在 ltask 中又分出了独占线程和共享工作线程两种不同的线程,可以把不同的服务绑在不同的线程上。甚至对于 iOS ,还必须让窗口线程运行在主线程上,而不得不在 ltask 里做特殊的支持。

最近发现的这个问题也是游戏客户端特有的,它很能说明用于游戏服务器的 skynet 和用于客户端的 ltask 在实现侧重点上的不同。

July 05, 2023

ttf 字体的一点问题

我们的游戏引擎使用的是 stb 的 truetype 库 来处理 ttf 字体的。最近发现在使用公司提供的 阿里巴巴普惠体 时出了一点问题。

引擎渲染出来的汉字比标称的像素高度矮了不少,想渲染 100 像素高的汉字,结果只有 70+ 像素左右。我们之前测试使用的中文字体( Windows 自带默认字体)没有这个问题。

在网上翻了一下,找到这么一个帖子:https://github.com/nothings/stb/issues/689 以及 imgui 也遇到过类似问题

April 12, 2023

SQL Server 向 MySQL 的迁移方案

昨天做内部晋升评审时听到候选人介绍他即将开始的一个项目。大致是我们公司从韩国买过来一个游戏(有全部源码)打算自己运营。该游戏服务器全部用 C++ 编写,使用 SQL Server 做数据库。我们这个项目,除了需要根据市场做二次开发外,还希望把 SQL Server 迁移到 MySQL 上。成本是最主要的原因,如果可以迁移成功,成本将减半甚至更多。该成本差异主要在 SQL Server 的使用执照费用,以及同等任务所需要的云成本。

我在反复确认了这项工作是否真的有做的价值(从 SQL Server 改为 MySQL)后,讨论了一下具体实施方案。

移植的难点在于该项目大量使用了 SQL Server 的存储过程。几乎所有的业务逻辑都是人肉写在 SQL Server 的存储过程中的,大约有 20 万行。虽然 SQL Server 的存储过程和 MySQL 的差异不是特别大,但再靠人一点点重写成本上也不划算。

August 24, 2021

内存对齐问题和编译器优化

昨天在公司内部的“不作不死”(程序员)群里,有同学贴了个知乎上的帖子 。表示这个问题居然关闭 gcc 的 builtin-memset 就解决了,感觉很玄学。

我说,这个感觉才是对的。关于文章中表达的 “添加编译选项-no-builtin-memset后,一切就正常了。然后大家都如释重负,不但解决了问题,又学到的新知识。” ,我认为这“如释重负”对于程序员来说才是种不正常的感觉,正常应该是“更加困扰”了才对呀。

到底是怎么回事,文章线索不全,无法判断。不过我直觉上感觉和我前几个月在我们这个“不作不死”群里讨论过的另一个问题非常相似。

May 20, 2021

ANSI escape code 及 Lua 封装

这两天想给一个想法做个简单的原型,因为涉及人机交互,需要在屏幕上绘制一些简单的交互元素。当然,现在有很多工具可供利用。过去遇到这种事情,我会尝试用已有的各种开源游戏引擎(我尤其推荐 PICO-8),或是直接在浏览器中用 css/javascript 写等等。

最近几年我玩了大量 RogueLike ,想尝试一下在 console 下用 ascii 字符来拼凑画面。这很有趣,能让我回忆起小时候 Apple ][ 上花掉的大把时光。同时我想用 Lua 来做开发,却不想引入 ncurses 这样的第三方库。最好能用几分钟就可以从零搭建起开发环境。

最好的选择当然是用 ANSI escape code 通过标准输出在 console 界面上作画了。

June 10, 2019

下划线命名和驼峰命名

其实我不是很在意代码的版式,比如到底用空格缩进还是 Tab ;花括号应该放在行末还是另起一行;以及,当需要用多个单词命名的时候,是用下划线分割还是驼峰。自己写的时候,自然有个习惯,但是项目中如果有多人参与,我也不在意大家各用各的。

毕竟是外在的东西,对代码结构没什么影响。甚至不太影响可读性。反而不同的风格容易区分出作者,阅读的时候更容易追溯到人,甚至比在 git 上看 blame 还方便一点。

前两天纠结这个命名法的问题,是因为在 bgfxidl 这个项目上,bgfx 的作者希望用 idl 描述并生成宏定义。我在设计对应的 idl 语法时涉及了到底采用下划线命名还是驼峰命名的选择。

May 22, 2018

Ericsson Texture 压缩贴图 EAC 的编码器

最近在做新引擎 UI 模块的工作。汉字字体纹理需要占比较大的一张贴图,考虑到这张贴图只需要用一个通道就够了,所以我决定使用压缩贴图。在手机设备上,GL_COMPRESSED_R11_EAC 是一个不错的选择。

EAC 是 Ericsson 提出的对单通道贴图的压缩方案,现已进入 OpenGL 的官方标准。它通常会结合 ETC2 一起使用。ETC2 负责 RGB 部分,EAC 负责 Alpha 通道。偶尔也可以单独使用。它会将每个像素解码为 [0,2047] 的整数,有 11bits 的精度,故而被称为 R11_EAC 。不过,我阅读文档后发现,其实有效精度是 8 bits ,一般也是从 8bits 的原始数据中编码得到的。压缩后,每 4 * 4 = 16 个像素会被编码为 64 bits ,压缩比 2 : 1 。用于字体纹理的话,可以节省一半的显存空间。

May 04, 2012

开发笔记(18) : 读写锁与线程安全

最近一段时间,我正解决的技术问题都是和多线程以及并发有关。

我期望在产品上线后,可以用的上至少 32 核的机器。这样,让多核平摊计算负荷就比较重要。单机承载的人数可以不高,但不希望玩家卡在一起特定的操作上。

这里强调的是单机上的并发,而把跑在不同机器的进程会分开考虑。所以,尽量利用共享内存以及单机锁来解决信息交换和状态同步问题。

上个月花了很多时间解决在 Lua 中并发读取同一份配置数据 的问题。一开始是想用一个 Lua State 储存一份结构化数据,然后让其它的 State 对他进行并发的读操作。我以为这种并发读的行为是线程安全的,但是我错了。因为读 Lua State 是需要对 Lua Stack 做修改,所以在没有锁的情况并不能做到线程安全。

随之我实现了一个改进方案。一开始就初始化好若干 Lua Thread ,在不同的 OS Thread 中使用独立的 Stack 空间操作。因为我们的框架只会启动有限的 OS 线程来跑更多的 Lua State ,这样做是可行且线程安全的。不过还是有一点小问题。如果用户向 Lua State 压入并不存在的 key (string 类型) ,修改 Lua 中 string pool 的操作又有线程安全问题了。当然,这个问题还可以用更为复杂的方法规避(比如再给每个线程配置一个独立的 state 做 string 合法性校验)。

我最后放弃了直接用 Lua State 储存这份共享数据,而改用自己实现的一个线程读安全的 hash 表。Lua 仅作为数据解析和加载过程的工具而存在。整套代码我已经开源了

February 21, 2012

开发笔记 (11) : 组播服务

最近一口气招了 5 个程序员, 在他们没有到岗之前,我不想把自己过多陷入游戏实现的细节上面。所以,除了维护一些前面的代码,陆续发现和修复一些 bug 外,我计划再完善一些基础设施的设计。这些基础模块暂时可以没有,如果实现好了又可以直接加入现有系统。这样比较利于后面的工作划分。

其中之一是服务器组播的模块。

先回顾一下前面提到的服务器架构(skynet ),它可以解决各个服务节点的命名问题,以及把消息从一个节点发送到另一个节点的能力。在不改变接口协议的前提下,最简单实现组播的方法就是把一个组命名为一个节点,由这个节点负责群法消息。

其工作原理类似于 UDP 的局域网组播。我还在网易时,实现过另一个类似的服务,见这篇 blog 。这次重新在另一个系统结构上做,有稍许不同。

February 14, 2012

开发笔记 (10) :内存数据库

离上一次写 开发笔记 快有一个月了。当然,中间我们放了 10 天的长假。

项目的进展比较缓慢、主要是解决一些琐碎的技术问题,客户端的比较多,服务器这边就是节前的一些 bug 修改和功能完善。大部分工作都不是我自己在做。由于感到人手不足,小规模私下的做了一点点招聘工作。也算物色到一两个同学可以过来一起干的。好久没做招聘工作了,都不知道怎么开始谈。唉,我们这里条件不算好,要求还多,都不好意思开口。

可写的东西其实也不少。今天挑一点来记录一下。

话要说回 开发笔记第六篇 ,我谈过结构化数据的共享存储。这个模块细化其实有挺多工作要做。核心部分我自己完成了,没有用太多时间。然后,有位华南理工的同学想来实习。想到他们学校距离我们办公室仅有十分钟步行距离,我便考虑让 logicouter 同学过来试试接手这个模块,做后续开发。当然,他不能全职来做,对旧代码也需要一定时间熟悉,进度比较慢。

我的规划大约是这样的:

核心部分仅仅实现了结构化数据在内存中的表达。但储存在内存中的数据还不能直接使用。C API 虽然实现好了,但性能比较低。我实现的是无锁的数据结构,采用单向链表保存。检索一项属性值都是 O(n) 的复杂度。这显然是不能在项目中直接使用的。为了提供性能,需要再做一层 cache 。在 Lua 虚拟机中,用 hash 表映射。使得读写数据的速度都降低到 O(1) 。因为我自己对 Lua 比较熟悉,所以这步 Lua 的薄封装还是我自己完成的。实测下来,和原生的 Lua 表访问差距不到一个数量级(3,4 倍左右),是可以接受的范围。比常规 IPC 通讯要快的多,也没有异步通讯的负担。以后编写逻辑代码的时候稍微注意一点就好了。

需要额外开发的工作是,要定义一个数据描述的小语言。类似 C 语言的结构定义。在数据储存中,我没有实现无格式信息的字典类型。 map 的 key 都是在结构定义中预先定义好的,内存中存放的是编号。这一是因为实现简单,而是可以实现成无锁的数据结构。再就是数据结构也能严谨一些,减少 typo (可以立刻检查到)。

May 25, 2011

电子书平台及英文阅读

这个想法是大约一个月以前细化的。类似的想法我在 97 年就有,当时网络并不普及,我为之做了一个单机版的 DOS 小程序。放在 cfido 上传播,以及放在个人网站上,有零星的几个用户。

我设想有这样一个工具,可以辅助我阅读英文小说,或是论文。它不应该是一个简单的词典,我不喜欢在阅读时有过多的交互。我希望作为阅读者,只是单方面的输入信息。那么,一个好的工具应该了解我的熟悉的单词领域,知道那些对我陌生的单词。并且明白,如何解释他们我可以明白其含义。

有时候,我只需要一个简单的中英文词汇对应关系;有时候我需要更多的解释。

一个好的辅助工具,能做到,在我需要简单解释的时候,在生词后面打上括号,写上对应的中文词。并在我尚未遗忘之前,同样的词不再注释。对于复杂的词汇,我希望它能排版在同一页的边侧,我第一感觉它应该在的地方。而不是让我费力的移动一下鼠标选中。

May 20, 2011

扯两句电厂经理

不知道我的读者里有多少桌游玩家。昨天我写那篇软件项目需要很多人一起完成可能是一个骗局 莫明其妙的就想到了 Power Grid: Factory Manager 这款游戏。

这是款我个人比较喜欢的偏计算类的游戏。玩家需要安排 5 个回合的工厂生产计划。购买机器、扩建仓库、发电。玩家需要支付电力成本,如果人手不够,需要雇佣临时工。偶尔,会因为场地不够扩建厂房。但是游戏只有固定 5 个回合。结束后,看谁赚到的钱多。当然,固定资产是不折现的。

May 19, 2011

软件项目需要很多人一起完成可能是一个骗局

本文的标题只是一个猜想,并不是我坚信的观点。事实上,我这几年自觉学到的重要东西之一,就是如何在开发过程中分工,如何信任队友开发的组件,如何组织许多人做同一个项目。

可是,如果这是一个骗局呢?那也未尝不是一种可能。

这个世界上我们需要做的软件可能没有太多真正庞大到需要很多人合作才做的出来。需要配置产品经理,需要设计人员,需要前端开发,后端开发等等。

更多时候,你需要很多人一起来完成仅仅是因为别人都这样在做。或者是,你缺乏某方面的专业知识,需要属于这个领域的人。又或者是有些工作很枯燥,你需要一个只是打工的人来帮你完成这些枯燥的你不想干的部分。也可能是你的老板觉得你进度太慢,觉得必须想办法加快进度,他觉得增加人手或许可以……

February 16, 2011

食堂排队系统

今天晚上 18:00 准点去一楼食堂吃饭,结果队伍排到了门口还打了个圈。花了 20 多分钟才领到口粮,回头一看,队伍并没有减短。看着前后都有同学抱着 PSP 狩猎 MHP3,我有点后悔没把 PSP 带下来了。

从这几天的经历来看,我们公司食堂的处理能力应该是够的。从 17:30 到 18:45 基本能处理完 600 号人的进食问题。也不能完全怪发放食物的带宽不够,经过一个月的观察,这部分可以优化的余地不大。而且我认为优化的意义也不大。

那么怎么才能减少这么多无谓的排队时间呢?粗看可能性不大,但仔细想想,如果把眼界放宽到整个系统办公楼运作系统,应该是有可能的。

在不提高食堂带宽的前提下,如果用户随机抵达,理论上队伍总会排到 200 开外。并不会在某个特定时间点减少(只有等到最后队伍才可能逐步缩短)。但我们其实可以把排队时间挪一部分到食堂外面的。行政部以前分发过通知,安排各个部门错开时间用餐也是这个想法。

今天一边排队,一边跟前后的同学讨论是否有可能利用软件来节约大家的时间。

January 26, 2011

极不和谐的 fork 多线程程序

继续前几天的话题。做梦幻西游服务器优化的事情。以往的代码,定期存盘的工作分两个步骤,把 VM 里的动态数据序列化,然后把序列化后的数据写盘。这两个步骤,序列化工作并没有独立在单独线程/进程里做,而是放在主线程的。IO 部分则在一个独立进程中。

序列化任务是个繁琐的过程。非常耗时(相对于 MMORPG 这个需要对用户请求快速反应的环境)。当玩家同时在线人数升高时,一个简便的优化方法是把整个序列化任务分步完成,分摊到多个心跳内。这里虽然有一些数据一致性问题,但也有不同的手段解决。

但是,在线人数达到一定后,序列化过程依然会对系统性能造成较大影响。在做定期存盘时,玩家的输入反应速度明显变大。表现得是游戏服务器周期性的卡。为了缓解这一点,我希望改造系统,把序列化任务分离到独立进程去做。

方法倒是很简单,在定期存盘一刻,调用 fork ,然后在子进程中慢慢的做序列化工作。(可以考虑使用 nice)做完后,再把数据交到 IO 进程写盘。不过鉴于我们前期设计的问题,具体实现中,我需要通过共享内存把序列化结果交还父进程,由父进程送去 IO 进程。

因为 fork 会产生一个内存快照,所以甚至没有数据一致性问题。这应该是一个网络游戏用到的常见模式。

可问题就出在于,经过历史变迁,我们的服务器已经使用了多线程,这使得 fork 子进程的做法变的不那么可靠,需要自己推敲一下。

January 06, 2011

梦幻西游服务器的优化

在历史工程上修补是件麻烦的事情。

前两天说起梦幻西游服务器的优化。这几天我到广州住下来,打算专门花一周时间搞定这件事。由于以前都是网上聊天,只有坐到一起才能真正理解问题。

目前,梦幻西游,只使用单台机器,最高配置 8 个 CPU ,配置 8G 内存。就算最热闹的服务器,也用不完这些资源(大约只用满了 3 个 CPU ,一半的内存)。核心程序差不多就是 10 年前写的,从大话西游延续至今。这两年一直在享受免费的午餐,随着硬件配置提升,现在单台服务器同时在线容量达到一万两千人。观察服务器回应速度的图表可以发现,目前的问题在于,定期会出现反应迟钝的现象。周期性的,服务器回应时间会超过 1000ms 。查得原因在于那个时候,磁盘 IO 非常拥塞。有定期保存玩家数据的服务对 IO 的占用,以及 SA 做的定期备份数据的脚本占用了大量的 IO 时间。最终造成了机器负荷过重。

IO 负荷过重最终怎样影响到游戏服务的性能,这个暂时不过于深入探讨。我这两天主要是分析以有的系统结构,并想一下改进方案。

November 05, 2010

多进程资源共享及多样化加载

梦幻西游在去年出了个新版本,在这个版本中,采用了 3d 技术渲染人物。我参加过部分的技术讨论。总的来说,对于公司的关键产品,是以稳定性为第一。所以不希望对程序做大改动。最好以独立扩充的模块为主。所以最终采用的技术是用 3d 技术渲染成图片,再依靠旧的程序框架跑起来。

采用 3d 渲染,主要是为了解决人物换装时的图片资源组合爆炸问题。当然还有更绚的特效等。

最近,梦幻西游的项目经理提出,新的版本性能依旧有些问题。当老的版本可以同时打开 5 个客户端时,新的版本只能开两个。允许用户同时开多个客户端,对梦幻西游这款产品非常重要。我最近几天就开始着力解决这个优化问题。

November 04, 2010

关于群服务的实现

既然好多朋友感兴趣,我就继续写写我对 IM 服务的看法。

许多人都认为 QQ 比较粘人的设计是群。我个人是反感“群"这个设定的,以前写 blog 批判过。不过我不否定用户的需求。

我认为,任何支持 XMPP 协议的 IM ,都可以利用 XMPP 的 s2s 服务做出一个独立于所有 IM 提供商的独立的群服务出来。

我想这个形式应该是这样的:

用户只需要加一个叫 群号@groupchat.foobar 的好友。由 groupchat.foobar 服务器提供 XMPP s2s 的协议。服务器指派一个机器人管理这个指定群。然后由这个聊天机器人来负责转发信息就好了。

这有点像 wow 做团队 DKP 工具一样。

可以设计一个人阅读方便的文本协议,不通过特定的 client 来解析,就能模拟出很好的用户体验的群聊服务。如果配上特定的 client ,用户体验会更好。

关键在于,这个服务可以做的很开放,不限制是由什么 IM 来使用。只要你用的 IM 服务支持 XMPP 即可。把 qq 群转移到这个上面也不会是太难的事情。而且这个群聊服务(groupchat.foobar)谁都可以做,并可以共存。技术牛的可以不限制群的人数,承载能力差的可以限制用户。有能力的可以提供 web 界面的聊天记录备查。

IM 协议只有到了互联互通那天,才能更好的激发程序员们的创造力,给用户更好的 IM 体验。

QQ 用户关系的迁移

最近网上最热的话题算是 QQ 和 360 的火并了。我不是 360 的用户,也没有装 QQ 的 client 。偶尔有朋友非要用 QQ 联系我时,我会用 web qq 登陆上去,收个消息,回复一下,然后就下来。我有个 6 位 qq 号码(曾经还有个 5 位的),04 年开始就不怎么用了。

在腾讯,我有几个私交很不错的朋友,对这家公司没什么恶感。唯一的一次是觉得他们不尊重 GPL 协议。所以这次这件事情也没啥心情观看。直到,QQ 居然封掉了 web qq ,用这种自残的方式参战,完全不顾用户的感觉。

我一直认为,做一家大的互联网企业是应该有更高的追求的。所以我喜爱 google ,他们以让信息被更高效的获取为己任。而作为中国 IM 最大提供商的 qq 呢?我想,更方便的让中国互联网用户相互联络应该是他们最高的追求吧。但是,qq 没有这个意识,这是让我最为失望的地方。

腾讯绝对有技术实力,按 XMPP 协议实现个互联互通的 s2s 服务。这能极大的促进互联网用户的在线交流。但是他们没有。这是我不喜欢这家公司的主要地方。而不是因为 qq 是我所在网易公司所开发的不争气的 popo 的竞争对手的缘故。

September 02, 2010

backtrace-mingw 更新

backtrace-mingw 今天更新了一下。原来的版本不能正确显示 dll 里的符号信息。现在可以了。只是打了个补丁,所以代码比较乱。

不知道 backtrace-mingw 的同学,可以看这里

August 31, 2010

在游戏引擎中播放视频

美术同学给我们的游戏做了段片头视频,正要加到产品中去时,才发现我们的引擎居然没有提供视频播放的功能。我想这个东西开源库一大堆,那做起来还不是小菜一碟。可没想到还是折腾了一整天才搞定。

第一件事是考察 License 。结果用的人做多的 ffmpeg 是 GPL 的,不适合我这种商业应用。虽然有一部分功能可以以 LGPL 的方式使用,但是遵守起来也是麻烦一大堆。想了一下还是做罢。我可不想学暴风影音和 QQ Player 那样上耻辱榜

最终考察结果,居然没太多的选择。还是 google 的 vp8 最好。License 最为宽松,所以就去下载了一份 libvpx 的最新版试用。

August 28, 2010

记一个 Bug

今天周末,桌游店里却没客人,昨天打电话预约的朋友没来,所以我就奔到办公室测试上周写的代码。

上周的工作主要是设计了一个新的包格式,然后整合入前段时间实现的虚拟文件系统中。

这个工作和前段实现的 zipfs 有相似之处,所以做起来也很快。不过前面没仔细测试。今天比较闲,就设计了几组复杂的测试数据,感觉覆盖了各种边界情况。一测试果然发现了 Bug 。

这个 Bug 有点启发意义,所以在解决掉之后,决定记录一下。

July 28, 2010

mingw 下的 stack backtrace

我们的项目的 Windows 版本是用 MinGW 开发的。当程序在 Windows 下挂掉后,固然可以用 gdb 调试,看到调用栈。但有些时候还是不够方便。

比如说今天,我们写的模型编辑器发到广州美术同事使用时,就出了问题。3d 程序在不同显卡环境下的确容易出故障,异地调试程序非常困难。这个时候,多么想看看调用栈啊。

The GNU C Library 是提供了 Backtraces 的,可惜 MinGW 不支持 :( 。最后打算自己写一个。

June 29, 2010

区分一个包含汉字的字符串是 UTF-8 还是 GBK

今天检查 svn 仓库,发现又有同学没按规定提交包含汉字的代码。我们规律,所有源文件中包含的汉字必须使用 UTF-8 编码方式,而不能使用 GBK 。

总这么人工检查也不是个事。所以我想写一个 svn 的钩子,在提交前检查。在仓库的 hooks/pre-commit.teml 加一行检查脚本应该就可以了。

我想用正则表达式匹配一下,可是想了想又觉得 UTF-8 和 GBK 的编码集有点交集,不太好做。btw, google 了一下,的确有人写过特定编码的正则表达式

继续 google ,找到一篇跟我需求有点类似的文章UTF-8编码检测失败特例。看了正文,觉得不太靠谱,然后继续看回复,觉得这方法可行。

然后定睛一看,原来文章是孟岩写的,回复是我自己三年多前回复在他的 blog 上的。 -_-

September 22, 2009

关于 GMA500 这块显卡

我的本配的 Intel 板载 GMA500 的显卡。当时选机器的时候就考虑过显卡因素,性能不重要,但是驱动要支持 opengl 2.0 ,这样至少我可以在笔记本上有限的调试下程序。wikipedia 上说 GMA500 支持 Opengl 2.0 我就信了。

拿到机器后,很是一阵折腾,把 Linux 下驱动装好。果然可以查询到 Opengl 2.0 ,不过支持的不算太多,或者说我们的程序没有彻底的测试,有些写的不规范的地方,不是那么顺利的可以跑起来。还需要慢慢改。

Windows 下是彻底看不见 Opengl 了。有点不甘心。

August 10, 2009

《程序员修炼之道》书评

一切阅读都是误读

—— 安伯托·艾柯

上次读这本书已经是在五年前。中文版刚出版我就买了一本。那个时候我的工作相对比较清闲,有大量的时间阅读。恰巧我在负责公司的校园招聘以及新员工培训,非常需要一些不错的教材,更早的时候听说过这本书的英文版,但是没能一读,中文版自是不能放过。另外,那年我在写书,记录一些程序员生涯中的心得,对经验的总结都颇有兴趣。

爱不释手,是我第一次读完后的心境。完整的经历了人生中第一个成功的大的软件项目后,我有许多感慨。不少东西知道怎么做对,怎样做不对,但是要一条条写下来,却不知道怎么总结。这本书说出了许多我想说,而不知道该怎么说的道理。

接下来的日子,我在公司做过好几次技术培训,课题都是以这本书中的某个或某几个观点,结合自己的经历展开的。对于信任我的同学,我总是把这本书列在给他们开的书单的第一本。

后来,国内又翻译引进了几本类似的好书。比如《代码大全》,《Unix 编程艺术》。古人云,读书有三上,马上、枕上、厕上。我还真把书买了好几本,床头、办公桌上各置一本,方便睡前、如厕时阅读;手机里放入电子版,上下班路上,偶尔翻阅。这些书的确是值得逐章挑选出来,反复精读的。《程序员修炼之道》却于几年前推荐给新入职的同事,从我的视野里消失了。

这几天,同事把书还了我,加上 周筠老师 发给我电子版,我又重读了一遍。原以为那些嚼烂了的东西,不会再有新味道,但是我错了。

不同的人从不同的角度用不同的方式,阐述相同的道理。其中细微的差异,是需要读者有了许多许多的经历后,才能体会的。比如,在《程序员修炼之道》中花了六页分析 DRY - Don't Repeat Yourself 原则;而在《Unix 编程艺术》中把它称作 SPOT - Single Point of Truth ,大约用了一页半的篇幅。他们真是是想表达完全一致的理念吗?我看未必。所以,作为读者,同样会有许许多多的想法。随着编程经历的越来越多,思考次数的增加,重新和这些前辈的思想相印证,也是一件乐事。

我们以为理解了作者,其实是误解。但我们将再一次理解编程。

August 08, 2009

Ubuntu 升级内核后不能正常引导的问题

这不是个新问题了。前两月开始,自从我的一台 Ubuntu 机器某次更新内核到 2.6.28-13 后,就无法正确引导。而只能引导旧的 2.6.28-11 。

这两天内核又升级到 2.6.28-14 问题依旧。

启动时显示:

Error 13 : Invalid or unsupported executable format

我觉得不能将就了,就花了点时间研究解决了一下。

August 01, 2009

捣糨糊

这两天完成一个新需求,需要维护以前一个同事写的代码。

前几天花了一天时间读代码,好不容易都看明白了,感觉真是一团糨糊啊。苦不堪言。

要在以前设计的接口基础上增加新功能,我觉得是件非常违背我的美学的事情。如果就这么干下去,我会做噩梦的。所以一咬牙决定重构。好在是比较底层的代码,只有中间层调用这些接口,和上层无关。

大约删除了 2000 多行代码,重写加上新实现的功能,用了 900 行左右。接口数量,加上新加的接口,总数减少到原来的一半。

另外修改了调用这些接口的大约 30 个源文件。昨天晚上完成的时候已经是一点,似乎都眼冒金星了,才把整个工程编译通过。

今天战战兢兢的测试两天闷头赶出来的战果,上午居然一点问题都没有,让我心虚的很。下午找到一个 bug ,稍微安心了点。晚上解决掉提交了。终于舒心了许多。

July 24, 2009

老人言

《The Elements of Programming Style 》是一本很古老的书。尽管 Fortran 我们不太使用,尽管新奇的语言层出不穷,但这些,30 年的岁月依旧无法掩盖其中的真知灼见。

英文版的 google 一下到处有,云风试着摘译几条。

  • 把代码写清楚,别耍小聪明。
  • 想干什么,讲的简单点、直接点。
  • 只要有可能,使用库函数。
  • 避免使用太多的临时变量。
  • “效率”不是牺牲清晰性的理由。
  • 让机器去干那些脏活。
  • 重复的表达式应该换成函数调用。
  • 加上括号、避免歧义。
  • 不要使用含糊不清的变量名。
  • 把不必要的分支去掉。
  • 使用语言的好特性,不要使用那些糟糕的特性。
  • 该用逻辑表达式的时候,不要使用过多的条件分支。
  • 如果逻辑表达式不好理解,就试着做下变形。
  • 选择让程序更简洁的数据表达形式。
  • 先用伪代码写,再翻译成你使用的语言。
  • 模块化。使用过程和函数。

June 28, 2009

玩了一下 ActionScript

周末。

玩了一下 ActionScript 。因为感觉做一些简单的需要长连接的互联网应用,flash 是一个不错的选择。在大多数情况下,比要求用户安装一个客户端要人性。(当然,和要求用户为浏览器安装一个莫名其妙的 ActiveX 控件相比,让用户自己决定是否下载独立客户端要友好的多)

因为,虽然 Flash 大多数情况下作为一个浏览器插件(在 Windows 下是一个 ActiveX 控件)的形式存在,但其安全性比之许多绿霸之流的流氓软件还是值得信任的。

June 01, 2009

《链接、装载与库》书评

今年二月份拿到这本书的电子稿时,还不是现在这个名字。

《程序员的自我修养》这个名字听起来比原来的那个名字感觉好一些,但又让人感觉有点不知所谓。还是副标题直接:《链接、装载与库》。我更愿意接受这样的一个名字,有如那本多年前读过的英文经典:《Linkers & Loaders》。

那段时间很忙,一直到现在都是。书稿我压了很久,直到有一天,博文的朋友说,约个时间和 Fenng 、俞甲子等杭州的程序员碰头聚一下。我连夜开始读书稿。不然,见面了谈起这本书来,说不出所以然多不好意思。

May 09, 2009

在文本模式下显示中文

办公室里我有两台桌面机,一台装的 Windows ,另一台装的 freeBSD 和 Ubuntu 双系统。上班的时候,两台机器我都开着,跑 freeBSD 的时候比较多。

在 freeBSD 下,我很少进 X 。主要是写服务器程序,或者是用来 ssh 到别的服务器上做管理。纯文本模式很清爽,速度很快,让人心情愉快。

唯一的烦恼是,有时候屏幕上有那么几个汉字显示不出来。有时是代码里的中文注释(所以我本人虽然英文极滥,也坚持用英文写注释),有时是别的机器上的一些文件名。

以前有同学向我推荐 zhcon ,类似以前 dos 下的中文系统。可这个玩意性能极低。用它我还不如直接用 X 呢。

周末,我花了两个小时写了个小程序,算是自己的解决方案。

March 23, 2009

编程的首要原则

刘未鹏的 blog 上写了一篇 编程的首要原则(s)是什么? ,这段时间在我的 google reader 上被许多人分享。

我问自己,我目前的首要原则是什么?

其实想说的,那篇里都有人说了。如果非要说首要,我也认可最多人认可的:

KISS - Keep It Simple Stupid

不过对 DRY - Don’t Repeat Yourself 我反而认为是次要的,当然是在和 KISS 相冲突的时候。

如果换一句和 KISS 原则相当分量的话,我会说:不要用愚蠢的方法做事。很矛盾?Repeat Yourself 往往代表了一些愚蠢的方案,且并不 simple ,至少会付出更多的体力。我想,KISS 的最后一个 S 指的是大智若愚的愚,而自做聪明则是另一种愚蠢。

在 KISS 的大原则下,我想其实可以分出一些细节的东西,也是别人都提过的:

March 17, 2009

让 GNU Make 把中间文件放到独立目录

今天想把项目用的 Makefile 整理一下,主要是让 .o .a 等等这些中间文件生成到独立目录中去。做这项工作的过程中,发现了一些有趣的问题。

当然最头痛的要属路径名的斜杠转换问题,这里暂时不提。为了描述方便,暂且不考虑 windows 平台。

GNU Make 处理斜杠的问题

GNU Make 对反斜杠的处理很恶心。用常规方法,你很难弄出单个反斜杠出来。

比如直接写 SLASH = \ 是不可以的,因为 \ 被用来连接下一行的。

而你用 SLASH = \\ 会生成两个反斜杠,而不是一个。

这个问题在 BSD Make 里没有,但在 GNU Make 里是个很让人头痛的问题,尤其是你需要做多平台的时候。

虽然 Windows 下,大多数时候,路径名中使用 \\ 等同于 \ 。但有的时候也会出些小问题,尤其在做很复杂的模式匹配的时候。

今天我下决心搞清楚这个问题怎么解决,最终 google 到了方法。

January 15, 2009

出在 recv 上的一个 bug

今天 卡牌对决 新版本发布,几乎都已经更新包放出来了,突然发现了 bug 。那个紧张哦,好在临危不乱 :) 用了半小时仔细阅读代码,找出了 bug 。

由于这次需要部署多个网络接入点,解决网通、电信、教育网互联的问题。我申请了各个网络的多台机器。新申请的机器是跑的 debian/linux ,而不是我们开发时用的 freebsd 。一个以前在 freebsd 上跑的正常的程序,放在 linux 出了问题,一开始让我很不解。

仔细查看后,发现 bug 出在一个 recv 调用上。用 gdb attach 到锁死的进程中,确认果然是一处 recv 调用没有返回。

December 23, 2008

一种对汉字更环保的 Unicode 编码方案

Windows 早期默认支持的中文编码方案是 GBK ,它是对早期 GB2312 的一个扩展。后来国家又发布了 GB18030 标准,扩展了 GBK ,增加了一些 Unicode 里才有的汉字,这样就可以做到和 Unicode 字符集做完全的映射了。

而后来 Unicode 逐渐成为标准,几乎所有的系统处理多国语言时,都把 Unicode 做为默认设置了。Unicode 有两套编码集,UCS-2 和 UCS-4 ,后者是对前者的补充,虽说字面说写的 4,但实际上只用到了 3 个字节不到。

Windows 的内部其实是用的 UCS-2 标准,并用 UTF-16 来实现。而非 Windows 系统大多采用了 UTF-8 。UTF-8 在很多情况下更有优势,首先它不需要考虑大头小头的问题,其次,数据损坏的时候,不会有半个汉字的问题。并且 UTF-8 可以完整的表达 UCS-4 而不需要有额外的付出。

前几年,处于 Windows 程序员转型期,经过一些痛苦的实践,我终于意识到,VC 提倡的所谓为软件维护 UNICODE 和 非 UNICODE 两个版本是件巨傻 X 的事情。之后,我们的程序再也不为内码分版本了,一律采用 UTF-8

不过,UTF-8 的设计明显是用英文为主的西方人搞出来的东西。对于中文一点都不环保。所有汉字和中文标点都需要 3 个字节才能表达。而少量欧洲字母可以用 2 字节表达,英文的 ASCII 符号则可以只用单字节。

我不指责这个设计,因为兼容 ASCII 是 UTF-8 的最大优势。其它,则是 KISS 原则下的产物。

不过,若是内部使用的话,如果你介意这 1/3 的储存空间的浪费的话,是不是有改进的余地呢?下面我们来讨论这个问题。

November 29, 2008

XMPP 简单研究

最近想做一个游戏服务器和 IM 互通的服务。最初的想法是可以增进游戏帐号的安全,比如游戏用户可以通过绑定一个 IM 帐号,从而不用登陆游戏就向游戏服务器发一些指令。这些指定通常是用来冻结一些帐号的功能。而游戏服务器也可以通过 IM 帐号向离线用户发送一些关键消息。这样,只需要解除绑定 IM 帐号需要一定的时间,或使用更安全的途径,即可以让游戏帐号更加安全。(至少,游戏用户可以从 IM 上获知他的游戏帐号每次登陆登出的时间、IP 等等)

后来细想,这里面可以做的东西还有许多。玩家会因为多一个信息通道,而更轻松的去玩那些需要长期驻留的游戏。游戏厂商也可以多一个挽留玩家的渠道,甚至用来宣传新游戏或游戏的增值服务,等等。好处不再列举。

其实、绑定 IM 帐号和绑定手机号本质上区别不大。只不过,IM 帐号几乎是零费用,又不像 SMS ,控制权掌控在移动手里。IM 更适合做双向交流(SMS 的双向交流不那么方便,而且对用户和游戏运营商都有经济负担)。独立提供一个 Game2IM 的服务供众多游戏运营商使用也是个有趣的主意。和 SMS 一样,只要给出一个简单接口让游戏运营商调用,把游戏网络和 IM 网络互联就可以了。

实现这个想法有两个方案。其一是制作各种 IM 的机器人,通过机器人和用户 IM 沟通。这个方案技术门槛稍低,有许多现成的机器人可以使用。缺点是,受 IM 提供商的限制(比如好友数量限制)。无法使用机器人的签名针对性的向用户传递特有的消息。除非你为每个游戏用户定制一个机器人,但那样,每个机器人都需要单独一个连接,对资源消耗过大。

第二个方案就是使用已有的 IM 互通方案,自己提供一个特有的 Game-IM 网络,跟已有的 IM 网络互通。比较流行的 IM 互通协议用基于 SIP 的 SIMPLE 和起源于 Jabber 的 XMPP 。

我最常用的 IM 是 google talk ,本身就实现了标准的 XMPP Client 和 XMPP Server 协议;而我们的 网易 popo 也实现了 XMPP 的 s2s 网关。我想研究一下 XMPP 是个不错的选择。

November 10, 2008

今年的 SD 2.0 大会

今年的 SD 2.0 大会据说还是在那个荒郊野外的九华山庄开。我们这里一个打算去的同事都有意见了,好不容易去趟北京,连六环都没进去。

前段时间孟岩让我准备一下,我想不出啥好玩的题目。后来就想聊点基本的东西,也是最近两年开发中纠结了很久总结出的丁点经验吧,有关内存管理的问题。

主题是关于:用 C/C++ 构建的系统,在内存管理这个层面,如何促进系统的健壮性和性能。session 的题目最终被命名为:高性能健壮系统中的内存管理 。有点绕口。

这两周一直很忙,直到前两天搜到今年大会的课程单 才发现,原来我的题目跟大家的相比,还真的老土了 :) 我想有点违背所谓 2.0 的宗旨。或许在十年前讲这个问题,感兴趣的人会多一些?呵呵,无所谓了,开会主要以交朋友为主嘛。

August 06, 2008

被 Darcs 折磨了一天

最近想在子项目中试一下 darcs ,替代原来的 svn 。只是尝试一下。

之前都是在本地玩 darcs 的,没遇到多少问题。今天找了台 freebsd 的机器,做了一个集中的仓库。没想到遇到许多麻烦。

我的想法很简单,在集中仓库的机器上建一个专有用户,把几个项目相关人员的 key 都放进去,大家都可以通过 ssh 访问这台机器。那么所有人都可以方便的通过 darcs get/put/pull/push 操作仓库了。

从 freebsd 或 linux 上远程操作这个仓库都没有多少问题,问题出在 windows 上。windows 版的 darcs 在提交文件时,一旦 patch 过大,就很容易失败。弄了一天才把问题弄明白。

May 09, 2008

写了个简易的 web server

根据昨天留下来的思路 ,我今天做了个 web server 。只用于给本地程序做配置界面用。

这个想法其实是以前用 google desktop 时明白的,gds 和 google 很多桌面软件都用浏览器做配置界面。其实就是自己做了个简易的 web server 而已。我也不需要太多,支持 GET 即可。仅监听本地端口,本质上没碰网络。windows 都不会弹安全警告。整个代码用 C 写的,才 200 来行。

做成了 lua 的一个模块,require 进来即可用,很方便 :) 再用上点 ajax 技术,操作感也不错呢。

May 07, 2008

数值调整、模拟器、编辑器

最近做游戏数值有点头大。也研究了一些游戏的设定,有点心得。举个很小的例子来谈谈:

wow 里的护甲对物理伤害吸收是乘一个百分比的,其公式为:

min (护甲/(护甲 + 400 + 85 * 敌人等级) , 0.75)

怎样理解这样一个公式的内在含义?为什么会设置成这样?

January 25, 2008

版本控制系统再考察

鉴于迁移版本控制系统的工作量比较大(主要是培训成本),这几天我们工作室重新调整了下各自对 svn 仓库的管理权限,理了下以后的开发管理流程。最终决定继续把 svn 用下去(至少到项目第一阶段完成),希望比以前用的更好。

但出于个人兴趣,我继续考察了几个分布式 VCS/SCM ,并装了其中几个玩。个人直觉 Darcs 最好。不过从流行度讲,或许 Mercurial 更佳。

虽然近期网上似乎支持 Mercurial 的最多(包括乌龟版也做的最全面),但我还是找到一篇文章支持我的直觉。

Whose Distributed VCS Is The Most Distributed?

他写的算是有理有据了。

不过这篇 blog 成文于 2006 年 8 月,离现在已有些年头,在这个讯息万变的今天,每个活跃的开源软件都会不断的发展,大家姑且看之。至于我的个人意见,只是出于直觉,没什么可参考性。

January 22, 2008

分布式的版本控制工具

我最早接触的 SCM 工具是 vss ,但是没用几天(换工作到网易后)就迁移到了 cvs 。我自己大约用了一年后,公司集体从 cvs 迁移到了 svn 。领导这次大迁徙的大大说, svn 是一个更好的 cvs (确实是这样吗?据说有争议,但至少我感觉在多文件版本控制上 svn 比 cvs 方便,因为 cvs 无法保证多个文件同时提交的原子性)。

前几年,有人跟我争论过到底 vss 的加锁模式好,还是 cvs 的合并模式好。我觉得答案是不言而喻的,懒得争论。虽然在某些特殊环境上,我们偶尔需要加锁模式协同工作,但对于程序员的协作来说,无疑我们需要并行的工作。

我们在若干年前曾经淘汰过一次加锁的协作编码方式,而到了今天,是时候再做一些改变了。或许,分布式的版本控制工具才是未来的发展方向。我想,总有一天,cvs/svn 这类集中式版本控制工具会被淘汰掉的。

说说我的困扰吧,可能很多开发小组也遇到过。

  1. 我们禁止提交不能编译通过的代码,尽量不提交不能测试通过的代码。结果,对于很复杂的模块,有人几乎一个月都没提交过一次。他总是觉得程序还不太成熟,但几经修改的代码其实从来没有作版本控制。

  2. 有些模块由两个人合作编写,关系非常紧凑。有时候需要在两人之间交换一些代码,为了方便,大家通过代码仓库中转,结果在仓库中留下许多未完成的版本。

  3. 代码被用笔记本带回家,结果在家完成的部分无处可以提交。(为了安全,我们的代码仓库不能从外网访问)

  4. 某人写了一个模块,总是有 bug 没有修改完,而不敢提交。这个时候,另一个人希望协助他找问题,却没有合适的途径 share 那段完成了一半的模块。跑过去 XP 一下么?天哪,为什么我们这里每个人用的编辑器都不一样,还都爱用些特别个性的配色方案呢?

我们尝试过一些 work around 的解决方法,比如在本地自己创建仓库。托 TortoiseSVN 的福,这件事在 Windows 下做起来还是很简单的。可终归是多个仓库的管理,用的人始终感觉麻烦,而没有贯彻下去。

今天有同事问我,分布式版本控制工具到底跟我们现在在用的系统有什么区别。我想了一下回答说:它的本质就是在原有工具的基础上增加了一种方便的仓库合并功能。(哈,我接触这类东西时间不长,大家就当我胡说)

August 06, 2007

读了 google 的几篇论文

周末在家赖床,把玩我的 treo 手机,从 google reader 上看到 myan 大大新近介绍 google 的三篇论文的中译版

(插一句,google reader 的手机版做的相当不错,很烂的手机都可以方便的使用,界面简洁,节省 GPRS 流量,强烈推荐)

这三篇论文我都有所耳闻,GFS 的论文应该出来最早,前几年就有同事发过英文版的给我看过。MapReduce 耳边经常有人跟我提起。BigTable 听的比较少一点,但也知道大概是咋回事。

三篇论文中译本都没读过(更别说英文版我会仔细学习了)。我便躺在床上,捧着手机,细细品读。

July 16, 2007

关于 jpeg 文档的修订

还在大四的时候,曾经跟 sina 游戏制作论坛的人赌气,自己实现了一个 jpeg decoder 。大约是 99 年底的事情吧。

当时查阅了许多资料,才把 decode 程序完成,同时写了一篇 JPEG 简易文档 。蒙网友厚爱,这么多年来不断转载,也总有人写信询问细节。今天收到一封不同的 email ,质疑我写的这篇非原创,乃一篇译文,却未在文中提及。

说来惭愧,当初的确查阅了不少英文资料,所以这篇文档也起源于其中一篇。怪当年年少无知,没有在文中注明。今日重读 CRYX's note about the JPEG decoding algorithm ,时隔多年,恍如隔世。当初动笔的时候,不知如何组织,照搬了这篇的结构,并且 copy 了其中的图表和公式。一开始只想粗略翻译一下,后来自己写写程序的时候发现许多细节不明之处,又查阅了许多别的资料(已经想不起来了,无法一一注明),陆续补充进文档。后来又有诸网友指教,经历了数次修订。

前几年修订的时候,也曾想过注明参考文献。皆因时间久远,无法找回原始资料,不能如愿。今天特更新注明,以尊重原作者。

ps. 如今的中文图书市场较之当年已大为改善,关于 jpeg 的专著已有翻译图书《JPEG2000图像压缩基础、标准和实践》出版,对其有兴趣者可以一读。

June 21, 2007

平台无关的游戏引擎

一直以来,我们的新引擎一直以跨平台为设计目标。这倒不是说,我们有多重视非 Windows 平台的用户。只是我觉得,一个好的设计一定是很容易做到平台无关的。对于做跨平台开发这件事情,公司里支持的人寥寥。光老丁都几次三番劝我不要把精力花在无谓的地方。

唉,怎么说呢。写了这么多年程序,我一直把编写代码和设计软件作为一件很有趣的事情在做。所以我并不认为我做的一切是一种工作,它是我的玩具。早就不需要担心后半辈子的生活问题,所以没有人可以阻止我做想做的事情,更何况我认为良好的设计造就优秀的产品。今天看似多花的精力,日后慢慢的会给出回报。

回想当年做西游的客户端,我固执的把内存的占用量控制在 64M 左右,让低配置的机器也可以流畅的运行。为了达到这一点,当年多花了好多的精力。做内存中的精灵压缩,做地图的动态加载,做图象数据的 cache 和主动交换,改动许多我认为会更多占用内存的数据结构,阻止美术的一切可能过于消耗内存的设计。

这些在我离开西游的开发后的这么多年里,配合硬件的发展,往日做的那些,得以让后来的不断扩展玩法和美术资源可以稳定的进行。后期的开发维护人员可以用足够的东西来“浪费”。前期的种种约束和正是为了后期的无拘束扩展,直到这些项目顺利的走完生命期。

我希望几年之后,依旧有人感谢我当下做过的努力。

April 28, 2007

终于不用 VC 了

最近一年半做的主要项目是跨平台的。但是只是说说,还没真的去试着在别的平台上 run 起来。因为我们做的是二进制复用,目标模块文件是自定义格式,所以也不太在乎编译器。原计划是在 Windows 下开发,用 VC 编译的。

最近几天真正开始做跨平台了,想来想去,还是改用 gcc 的好 。废弃 VC 倒不是因为它不好,而是想买一台 Mac mini 放在家里用用 :D 一直家里都没买电脑,我也不用笔记本,回家就是打游戏和睡觉。到时候有了机器,在 Mac OS 上自然是没有 VC 用了。

所以,我的跨平台目标就定在了 win32 、freebsd 、linux 和 macosx 。当然,目前我的测试环境只有 win32 和 freebsd ,这几天就在把这两个搞定。