20220301程序思考随笔

一、关于游戏主循环的思考

灵感来源: 命令行与游戏引擎初探 我们讨论游戏的主循环。当然游戏的主循环之外,依然可以有一些维持游戏开始界面、重新开始的循环。但是这些不重要。我们只讨论游戏核心运转的主循环。

loop do
  eventOrGetInput

  updateState

  render
end

不论多复杂的游戏,哪怕像Unity这样。复杂的表象背后依然是这么个简单的游戏循环。我们必须得保证游戏循环的render部分要 比如 1秒更新 60次(60HZ),现在也有120次(120HZ)。

我们现在在透过表象思考游戏的本质。我们还知道游戏他是一帧一帧来工作的。这意味着比如一秒有120张画面被处理出来。

我想要表达的是不论多复杂的代码,最终运行的代码其实是少数,并且要缩短到 16.6ms 以及以内运行。很多的代码是旁路分支,他们可能运行不到。

另一个思考是,屏幕那么多点,如果拿CPU其实不断地运算,这是很难完成的。尤其是我在写了一个 snakes 之后,觉得扫描全部是一种非常愚蠢的决定。 但是我们要想到,还有一个叫做 GPU 的东西,他的内部可能存在几十个或者几百个这样的CPU运算核心(当然只负责简单的的、专门的图形计算)。对于 16.6ms 渲染计算那么多像素,交给几百个计算单元的GPU,这样子似乎就很容易理解了。

不过我们还是直观的感受下 CPU 和 GPU 的区别。可以看这里 B站:The Mythbusters, Adam Savage and Jamie Hyneman demonstrate the power of GPU computing.,或者 Youtube: The Mythbusters, Adam Savage and Jamie Hyneman demonstrate the power of GPU computing.

我还有印象CPU和GPU的合作方式,是 CPU 负责逻辑和发布指令,GPU 负责密集的计算任务。直觉上这件事变得容易理解很多。

二、一切魔法来自于循环

有个游戏团队叫做“波兰蠢驴”,来形容他们对于技术和游戏的勤勤恳恳。程序给我的感觉是这样,他们无比精确,分秒不差。但是他们如此勤奋。永远在循环之中。

程序的三大结构—— 顺序、分支、循环。

我觉得只有循环,真正意义上赋予了程序种种“魔法”的感觉。程序总给人一种感觉——它很聪明,似乎已经知道了一切。而这一切的背后,是程序一遍又一遍在循环之中,不断地遍历、反复的判断信息,最终把所有情况都探索完毕,返回一个结论的过程。

每一轮的循环,就是在做一个具体的任务目标。大数据算法,就是不断地在拟合数据点,接近目标并且根据模型返回值。图像处理就是一遍又一遍扫描所有的像素点,去计算局部像素点他们和其他像素点的关系。这种计算量相当的大, 当然与之对应的是人类发明了更加强大的芯片。

之所以有循环概念的产生。那么我们理解的程序,不应该单单的是 —— 函数。一种从上执行到下得出计算结果的东西。他还应该包含循环,就是在时间维度上,我们可以有一轮一轮的计算,每一轮都可以相互影响,经过时间的流逝,得到的运算。

或者换个说法,计算机本质上依然是人类思考的记录。解决一个办法,并不是一定要像计算一个现成的数学问题一样,有明确的公式,你只要领悟到了这是一个可以用公式解决的,直接代入计算就解决了。而你解决问题的方式,可能就是去找这样一个公式。 还有另一种可能是 —— 就像人类一样,我们思考一些问题,研究一些问题,只能不断地去尝试、试错,在时间的流逝中寻找答案。 程序借助循环做的事情也差不多,他在所有可能的情况里面不断地计算,一遍又一遍,直到像走迷宫,我们把所有的路都试了一遍,自然找到最后的结果。

甚至可以这样说,这种方式才是计算机解决问题的方式。各种问题,终归用计算机都是遍历解决。只是聪明的遍历还是笨的遍历,差别在于聪明的遍历可以减少不必要的遍历罢了。

三、goto、循环和递归是可以彼此替代的

以前在学习C语言的时候,老师就说过不要用 goto,禁止用goto。 goto 会让一切程序变得难以理解。但是实际上,见识多了,你可能不相信,Redis 使用了很多所谓的 “禁忌” 从而编写出自己独特的高性能。而Linux 里面充满了大量的 goto, linus本人就喜欢用goto。

用或者不用根据情况、根据个人水平来决定。

我想说的是,我突然想到,一旦深入本质的思考,你会发现,其实goto依然存在,无处不在。

我们知道程序是存储在内存里的,有地址指针,顺序的读取程序执行。如果我们移动了读取代码的指针,这就是goto。但是循环其实本质上就是把指针重新指向了函数的开头罢了,这就是一种goto。

那我又想到了,我们经常说 循环和递归是可以彼此替代的。我觉得这里其实是有深层次原因的。

其实从运行角度来说,递归就是再一次的调用自己,本质上就是把运行指针再一次 goto 到自己罢了。就指针往返动作来说,确实和循环很像。

我们甚至可以猜到,循环、递归到底是如何在底层语言被实现 语义的呢? 那应该就是 goto了。

所以理论上,计算机的底层其实是 —— 顺序、分支、goto 。 而 goto 可以构造循环。

对了顺便提一嘴,ruby 里面的 catch\throw 语法其实就是 goto 了。

catch (:back_point) do
  # do some thing
  throw :back_point

end

四、z轴时间轴理解 循环在做什么

我有一个想法就是如何可视化理解循环。我们正在编写的代码,可以想象计算机从上往下阅读,遇到分支就分叉执行。遇到循环,就原地打转。但是我觉得其实不对。这样理解总会产生混乱。

遇到循环之处,应该沿着纸面向外建立一个 Z 轴,Z轴也是时间轴,循环的代码在Z轴上螺旋上升。

因为每个循环都是独立的作用域,执行在自己的栈帧上,并且状态会对外部产生影响。

这样就很生动。

总结

学而不思则罔,思而不学则殆。最近上班开车,而开车这四个小时里面,什么都不能动,也不能忙碌,反而能够思考很多东西,这是以前不曾有的体会。

Mark24

Everything can Mix.