“写笔记”支持四种格式——Word 文档、Excel 表格、Markdown、纯文本,起稿或二次编辑时都能随时切换,同一篇笔记想用哪种形态来记,都由你说了算。
md、txt、csv、json 这类纯文本则原样载入,不做多余加工。拿一张现成的表倒进来、改几笔、再导出去,等于白用一台免费的格式转换器。
要带走就在右上角点“下载”,可导出 PDF、Word、Markdown、Excel、TXT 等格式;列表卡片“⋯”菜单里,也有同样的下载入口。
在“工具”页点“+ 上传工具”即可发布:填好名称与链接,再用 Markdown 把使用方法写清楚——能解决什么问题、怎么装、怎么用,比堆介绍实在。
要分发安装包就一并上传压缩包(ZIP、RAR、7Z、TAR.GZ,最大 35MB),别人在详情页一键下载;只放链接不带附件也可以。
工具按大家的收藏热度排序,好用的自然会被顶上来。发布后可在详情页或卡片菜单里编辑、下架。
写笔记时勾上“隐藏”,这篇就只存在于你自己的账号里:不进列表、不进搜索、不上首页精选,也不会出现在任何公开的页面,链接发给别人同样打不开。
适合放密码、草稿、日记这类只给自己看的内容;想公开,去“发布”打开它,把“隐藏”的勾去掉再保存,之后编辑会默认保持原状态,不会悄悄变回公开。
你的内容会同时保存在多个副本上,系统定期做备份与完整性校验,再配合异地容灾机制:就算某台机器出问题,数据也不会丢,可以长期放心存放;特别重要的资料,仍建议你另外再留一份备份。
全站跑在容器化、模块化的现代架构上,更新、部署、回滚都很快,扩展性和稳定性都按长期运营的标准来设计(Built for reliability, designed to scale)。
这个网站最早只是一个人的笔记仓库,后来慢慢长成现在的知识中枢。设计上很克制——没有广告、没有追踪、没有推荐算法,只是干干净净地存放一些东西;既然做好了,就公开出来,万一有人用得上呢。
不做大而全,不做平台梦,保持简单、保持克制、保持好奇。所有内容都由用户贡献、由用户维护:不会突然冒出付费墙,不会在角落塞广告位,也不会把你的数据卖给第三方。
产品会持续迭代,站内日志页记录着每一次改动,改了什么都有迹可循;想了解这个站是怎么一步步走到今天的,翻翻日志就能看到来龙去脉。
如果在这里看到涉嫌违规的内容,点对应卡片右侧的“举报”按钮就能提交,我们会尽快核实处理;也谢谢你花一点时间,一起把这里维护干净。
How I cut GTA Online loading times by 70%
{"category":"游戏动漫","title_cn":"深入剖析:我是如何将GTA Online加载时间缩短70%的","content":"> 通过逆向工程和性能分析,找出GTA Online加载缓慢的根源——低效的JSON解析和二次方复杂度数组查找,并验证了修复方案。
去年,我重新拾起GTA Online,想完成几个新出的抢劫任务。不出所料,它的加载速度依然和七年前发布时一样慢得令人发指(反讽)。是时候一探究竟了。
初步侦察
首先,我检查了是否已有人解决此问题。我找到的大多数结果都指向一些道听途说:比如游戏太复杂所以加载慢、P2P网络架构垃圾(并非说它不垃圾)、各种从故事模式进入线上模式的复杂方法、或者跳过R星logo的Mod。进一步阅读后得知,将这些方法结合起来,我们最多能节省30秒!然而与此同时,在我的PC上……
- 故事模式加载时间:约 1 分 10 秒
- 线上模式加载时间:约 6 分钟(从R星logo到进入游戏,不含社交俱乐部登录时间)
我的配置虽老但尚可:AMD FX-8350 CPU、金士顿SA400S37120G入门级SSD、8GBx2 DDR3-1333内存、GTX 1070显卡。我知道配置过时了,但究竟是什么原因导致线上模式的加载时间是故事模式的6倍?我尝试了前辈们的故事转线上技巧,但无法测量到任何差异。即使有效,结果也在误差范围内。
问题并非个例
如果这个投票可信,那么该问题已影响超过80%的玩家,足以引起广泛不满。R星,这都7年了!
我研究了一下那些幸运的20%玩家是如何做到3分钟以内的加载时间。我找到了一些高端游戏PC的基准测试,线上模式加载时间约2分钟。我宁愿“黑”进去也要2分钟的加载时间!这看起来确实与硬件有关,但有些地方说不通——为什么它们的故事模式加载仍需近1分钟?而且,从故事模式加载到线上,他们只需多等1分钟,而我却要多等5分钟。我知道他们的硬件好得多,但绝不可能好5倍。
高度精确的测量
凭借任务管理器这样的强大工具,我开始调查哪个资源是瓶颈。
在花了一分钟加载故事模式和线上模式通用的资源后(这部分与高端PC相当),GTA决定在我机器的一个核心上满载运行4分钟,然后什么都不做。
- 磁盘使用率? 0%
- 网络使用率? 有一点,但几秒钟后就基本降为零(除了加载轮播信息条)。
- GPU使用率? 0%
- 内存使用率? 完全平坦……
这难道是在挖矿吗?我闻到了代码的味道。非常糟糕的代码。
单线程瓶颈
虽然我的老AMD CPU有8个核心,力量不弱,但毕竟是老古董了。当年AMD的单线程性能远落后于Intel。这可能无法解释全部的加载时间差异,但应该能解释大部分。奇怪的是,它只吃CPU。我原本预期会看到大量磁盘读取或网络请求来协调P2P会话。但这样?这很可能是个Bug。
性能剖析
性能分析器是查找CPU瓶颈的好工具。但有个问题——大多数分析器依赖插桩源码来获取进程内部情况的精确图像。我没有源码。我也不需要微秒级的精确读数——我的瓶颈持续足足4分钟。
于是,栈采样登场:对于闭源应用,唯有一个选项——按设定间隔转储运行中进程的调用栈和当前指令指针位置,构建调用树,然后汇总统计。
我知道的能在Windows上做到这点的分析器只有一个(可能我孤陋寡闻),而且它超过10年没更新了。它就是 Luke Stackwalker!拜托,哪位大神给它点爱吧!
通常Luke会合并相同的函数,但由于我没有调试符号,只能目测邻近的地址来判断是否是同一位置。我们看到了什么?不是1个瓶颈,而是2个!
深入兔子洞
借了我朋友那份完全正版的行业标准反汇编器(不,我真的买不起那玩意儿……总有一天我要学学Ghidra),我开始拆解GTA。
大多数知名游戏都内置防逆向工程保护,旨在阻止盗版、外挂和MOD制作者。但这从来都挡不住他们。看起来这里有某种混淆/加密机制,把大部分指令替换成了乱码。
别担心,我们只需在游戏执行目标代码时转储其内存即可。指令在执行前无论如何都需要去混淆。我手头有Process Dump,就用了它,不过也有很多其他工具可以做这件事。
问题一:这……是strlen?!
反汇编现在混淆度较低的转储后发现,其中一个地址的标签竟然是从某处提取的!是 strlen?沿着调用栈往下,下一个函数标签是 vscan_fn,后面的标签就没了,不过我相当确信那是 sscanf。
它在解析什么东西。解析什么?
理顺汇编代码会花很长时间,所以我决定用x64dbg从运行进程中转储一些样本。经过一番调试单步执行,结果发现它解析的是……JSON!整整10兆字节的JSON,包含约63,000个条目。
..., \"key\" : \"WP_WCT_TINT_21_t2_v9_n2\", \"price\" : 45000, \"statName\" : \"CHAR_KIT_FM_PURCHASE20\", \"storageType\" : \"BITFIELD\", \"bitShift\" : 7, \"bitSize\" : 1, \"category\" : [ \"CATEGORY_WEAPON_MOD\" ], ...
这是什么?从一些引用看,它似乎是“网络商店目录”的数据。我猜它包含了GTA Online中所有可购买物品和升级的清单。但是10兆?这不算大!用sscanf解析可能不是最优方案,但肯定不至于这么慢吧?
嗯……是的,这确实要花很长时间!(注:我承认自己并不知道大多数sprintf实现内部会调用strlen,所以不能完全责怪写这段代码的开发者。我以为它只是逐字节扫描并能在NULL处停下。)
问题二:用哈希……数组?!
第二个问题被发现在第一个问题旁边调用。在同一个if语句里,从这段丑陋的反编译代码可以看出:
(所有标签都是我标,不知道函数/参数的实际名称)
第二个问题?在解析完一个条目后,它被存储到一个数组(或者说内联的C++ list?不确定)中。每个条目大致长这样:
struct uint64_t *hash; item_t *item; entry;
但存储之前呢?它会遍历整个数组,逐个比较条目的哈希值,看这个条目是否已存在。对于约63,000个条目,如果我的数学没问题,那就是 (n^2+n)/2 = (63000^2+63000)/2 = 1,984,531,500 次检查。大部分是无用功。你有唯一的哈希值,为什么不用哈希映射(hash map)?我在逆向时给它命名为hashmap,但它显然不是哈希映射(not_a_hashmap)。
而且更有趣的是:加载JSON之前,这个哈希数组列表是空的。而JSON中的所有条目都是唯一的!他们根本不需要检查条目是否已存在!他们甚至有一个直接插入条目的函数!直接用那个不就完了!认真的吗,这什么玩意!?
概念验证
这固然不错,但除非我做个测试,否则没人会相信我能写个标题党帖子。计划如下:编写一个.dll文件,注入GTA,Hook某些函数,???,然后出成果。
JSON解析问题很棘手,我不可能真的去替换他们的解析器。用一个不依赖strlen的sscanf替换更现实。但有个更简单的方法——Hook strlen,等等……"}