linux网站服务器配置,ysl千人千色t9t9t90网页版,长沙住建信息中心网站,网站应用市场设计摘要#xff1a;将已有的上万行代码迁移至另一种编程语言#xff0c;从来就不是一件容易决定的事情#xff0c;而本文作者却信心满满地要将 5.8 万行代码全部用另一种不那么主流的语言重写#xff0c;这是为什么呢#xff1f;链接#xff1a;https://www.yet-another-blo…摘要将已有的上万行代码迁移至另一种编程语言从来就不是一件容易决定的事情而本文作者却信心满满地要将 5.8 万行代码全部用另一种不那么主流的语言重写这是为什么呢链接https://www.yet-another-blog.com/porting_the_game_to_jai_part0/声明本文为 CSDN 翻译未经允许禁止转载。作者 | Simon van Bernem 译者 | 弯月 责编 | 郑丽媛出品 | CSDNIDCSDNnews在这篇文章中我将分享把一款正在开发的游戏移植到 Jai 语言的经过。游戏本身主要是用 D 和 C 编写的总共有 58,620 行代码不包括库。原因你可能想问为什么要将如此大规模的程序移植到另一种编程语言你完全可以等到新项目再用新语言嘛我之所以移植这些代码原因主要有以下几个目前这些语言给我的日常工作带来了无尽的痛苦。我有合适的系统来帮助我移植代码所以我认为此次移植能够顺利进行。对于我来说Jai 似乎比 C 或 D 更具吸引力。最重要的原因是我喜欢 Jai为什么放弃 C网上有很多文章诉说了 C 的缺点所以我不打算在此赘述。简单来说C 几十年的发展积累了很多错误决定我们没有任何方法去摆脱它们。标准库是一场灾难使用其他人的代码也非常困难而且不知何故C 每次添加的新功能都有陷阱。虽然 C 的这些缺点也不至于糟糕到让人避之不及但它确实给我带来了很多痛苦。此外这些年来 C 的发展似乎不尽如人意我不觉得这门语言会越来越好所以我还是想逃离 C 生态系统。为什么放弃 D 语言2019 年当我开始开发这款游戏时已决定放弃 C但我不确定应该选用何种语言。最终我选择了 D 语言因为这门语言与 C 类似但没有 C 的那些缺点。然而不幸的是事实证明 D 语言也有一些 C 中不存在的缺点。虽然 D 语言有一些优点比如更强大的元编程、不需要头文件、没有未初始化的值等但相较于缺点而言这些优点不值一提。我在 Windows 的两款 D 编译器dmd 和 ldc2之间来回折腾了 4 年之久到头来却发现在 Windows 上编写 D 代码只适合个人的业余项目或早期不成熟的软件该语言完全不像有 20 多年的发展经历。就目前的情况来看我不建议任何人使用 D 语言在 Windows 开发正式项目。相较而言继续使用 C 才是更好的选择。根据我多年的经验来看在 Windows 编写 D 代码所面临的最大问题在于其调试信息千疮百孔90% 的情况下不会给出 this 指针或给出错误的 this 指针函数堆栈上的变量经常不完整、丢失或有误变量的值有时会报告错误却看不到任何其他问题静态 foreach 扩展的处理不当甚至会导致调试器紊乱mixins相当于 D 语言的宏生成的调试信息会导致调试器找不到正确的文件因此你需要逐步反汇编在 visual studio 中将指令指针移到上一行常常会导致程序在下一条指令上崩溃。网上有人告诉我一直以来 DMD 的调试信息就存在很多质量上的问题但不幸的是上述大部分问题不仅限于 DMDWindows 的两个编译器都有这些问题。除了调试之外元编程的核心部分还存在其他问题和缺点不同的编译阶段以奇怪的方式交互常常导致元编程出现意外同时还会产生具有误导性的错误ldc2 的编译速度非常慢但有时这款编译器是唯一的选择因为 DMD 有 bugD 提供了一种 betterC 模式其中包括禁用垃圾收集。然而在使用这种模式时标准库不会被编译而且元编程也会遇到重大问题缺少文档此外还有一堆小问题。总之虽然从某些方面来看D 确实比 C 略胜一筹但其他方面的小问题非常多累积起来导致使用 D 语言编程也非常痛苦。糟糕的调试信息和垃圾收集的需求很致命我的整体感受是D 的创始人对 C 的看法似乎与我截然不同。我只是希望改进 C而不是用一些 C 的问题来换取 D 的其他问题。现在我对调试器有严重的信任问题。为什么选择 JaiJai 是 Jonathan Blow 于 2014 年开发的一款编程语言而编译器一直到 2019 年 12 月才开始内测。如今封闭测试仍在进行中大约两个月前我应邀参加了这项测试。Jai 的设计初衷也是希望改进 C但与 D 语言不同Jai 正在对 C 做出有意义的改进。在我看来最重要的改进包括编译速度更快以及允许通过无限制的编译时执行来实现元编程。请注意这里我所说的编译速度提升可不止 20%而是 10100 倍而且你能在编译期间执行任何操作不仅限于元编程。尤其是元编程与编译时编译器 API 的结合使用具有深远的影响例如你不再需要构建系统或启用复杂的自定义检查。除此之外Jai 还对 C 进行了其他方面的改进比如更好的默认值、更简单的语法、更实用的标准库、命名函数参数、上下文、using 等等。我希望我的游戏从 D 移植到 Jai能够获得以下提升编译时间从现在的 60 秒减少到 5 秒以下争取能缩短至 1 秒左右调试器能够正常工作用 Jai 代码替换构建脚本使用元编程引入自定义编译检查以抓取更多错误用更简单的命令式代码替换复杂的元编程代码各种语法改进减少代码中的繁琐部分删除使用多种语言时不可避免的重复。我希望通过这篇文章记录我的期望将来可以回过头来检查有多少期望真的实现了。为什么不是其他语言如上所述我不喜欢 C 和 D 语言而 Jai 看起来很不错。那么其他编程语言呢似乎 Rust 也是一个很好的备选。这门语言风头正盛而且还有一个热心的社区但我个人认为 Rust 并没有做出正确的权衡。它的支持者都是唯“内存安全”是论者考虑到如此多的漏洞都是内存安全引发的我可以理解这种心态但他们忽略了其他高安全性、高质量的方法。例如我相信如果 C 和 C 没有零终止字符串、默认初始化值那么大部分漏洞都不会存在再加上合适的指针长度类型就可以用边界检查代码取代 90% 的指针计算然后再建立一种文化不鼓励大家自己想办法自行管理内存。除此之外我认为我们在寻找“安全的”代码时完全忽视了我们拥有如此多漏洞的最主要的因是我们的文化对复杂性的容忍甚至是鼓励。总之忍受 Rust 慢吞吞的编译并接受借用检查器是一种极端的解决方案并没有解决最重要的问题这是一个文化上的问题。另一方面Jai 非常在意复杂性并努力建立正确的文化。对于其他不太受欢迎的语言例如 Zig我只能说虽然它们可能具有巨大的潜力但我并无法相信它们是正确的选择。我不是说这些语言不好只是它们不适合我。移植方法在本文开头我曾说过我认为此次移植能够顺利进行原因是我的游戏中有两个系统对此次移植有很大的帮助我的游戏可以将游戏过程中的输入HID、加载的文件、网络等记录到一个文件中之后进行回放。在回放的过程中系统可以将记录下来的输入传递给游戏循环从而重现一模一样的游戏状态。玩游戏期间系统可以在各个时间点对游戏状态进行哈希处理并将这些哈希值保存到不同的文件中。在回放整个游戏过程时系统可以利用这些文件检查游戏的状态是否与原来匹配。实际的功能和上面的描述有一些细微的差别但不会影响整体的逻辑。根据这些特性我的移植计划如下将一小段代码从 D 或 C 复制到 Jai然后编译调用这段 Jai 代码回放录制的游戏会话如果回放出现分歧则说明移植引入了 bug修复 bug如果回放没有出现分歧则说明移植成功重复以上操作。关于该方法是否有效我需要考察两个关键问题移植引入的 bug 是否会导致游戏状态出现明显的分歧能否以少量、渐进式的方式移植代码这样在得知存在 bug 时更容易找到bug第一个问题取决于状态哈希覆盖了多少代码。一部分代码需要判断游戏是否正在回放这部分代码在回放时有不同的行为因此无法完成真正的哈希处理。例如写入文件的功能在回放时会直接丢弃所有数据因此如果移植在写入文件的代码时引入 bug则不会被哈希处理注意到。幸运的是大多数代码不属于这一类。最初只有很小一部分代码使用了哈希处理例如物理模拟但最近我设法进行了扩展在向动态数组插入数据时用哈希来记录其大小和容量。这意味着插入动态数组的代码中的 bug 很快就会被发现。由于动态数组的使用在我的代码中随处可见所以对于庞大且复杂的数学算法来说即便是一个很小的变化也能带来立竿见影的效果。第二个问题是一个经典的编程问题代码的解耦性如何这个问题非常有趣因为在移植的过程中我将亲眼目睹我的代码库中究竟封存了多少不为我所知的复杂性。一个明显的问题是模板函数这些代码无法直接移植因为函数的定义和调用必须在同一个编译器中模板才能发挥作用除非你手动实例化模板。我的代码库中有大量的模板化代码但大多不依赖于容器或序列化所以我希望不会引起太大的麻烦。移植过程下面这张图是移植前的代码库状态整个代码库有 45,701 行 D 代码和 12,919 行 C 代码总共 58,620 行。 编译时间如下在调试模式下ldc2 需要 3 分钟才能完成编译内存使用量峰值约为 8GB如果打开浏览器我笔记本电脑的 16GB 内存很容易就饱和了。发布模式更糟糕约为 11.5GB。为了记录移植进度我绘制了如下代码库的示意图如果一切按计划进行上面两张图中的颜色都会改变我非常期待最后我来说一下我期待的效果整个移植的过程需要 160 小时每周工作 40 小时一共需要一个月。编译时间从 1 分钟缩减到 5 秒以下理想值为 1 秒左右。