返回首页
最新
嗨,HN,
我是比利,ChatterBooth的联合创始人。这是一款匿名应用程序,人们可以在这里自由地交流或聊天,而不必担心被评判。
这个想法源于2020年,当时我的一位联合创始人想分享他母亲战胜癌症的经历,但却无法做到。这让我们意识到,在网上敞开心扉是多么困难。最终,我们在今年早些时候发布了这款应用。
我们还注意到,人们常常向AI聊天机器人倾诉,只是为了被倾听,但AI并不能真正理解你。ChatterBooth旨在促进真实的人际连接,即使是匿名的。
未来,如果我们获得足够的关注,我们希望能以“对话引导者”或“友善陌生人”的身份来奖励用户——这些都是鼓励连接的小举动。
我们在北美、欧洲和亚太地区的23个国家的iOS应用商店上线,目前已经有几百次下载。虽然还处于早期阶段,仍在自筹资金,但看到应用中真实而诚恳的时刻发生,令人感到惊喜。
如果你对此类事情感兴趣,或者你也开发过类似的东西,我很想听听你的想法。如果你愿意,可以查看我们的网站并加入我们的Discord。
感谢你的阅读。
我喜欢观察旧平台或软件的用户界面。那种像素化的外观对我来说比现代的用户界面更具吸引力。<p>对我来说,我最喜欢的一些界面是后期PalmPilot的用户界面。尽管只有160×160像素的显示效果,它们的表现却出乎意料地好。虽然今天看起来可能不算最佳,但它们非常实用。在某种程度上,我实际上更喜欢旧的用户界面设计。现代的用户界面组件感觉更复杂,在我看来,这导致了更多的bug。<p>大家最喜欢的用户界面是什么,无论是旧的还是现代的?
我一直在构建一个跨平台的 JSONL 查看器应用程序,能够处理多个 GB 的文件。在我的开发机器 macOS 上运行得非常完美,但在 Windows 上却在 2,650 KB 时总是崩溃。以下是我的调试过程以及那个微小的修复,它改变了一切。
<p>问题</p>
- macOS:轻松处理超过 5GB 的文件
- Windows:每次在 2,650 KB 时崩溃
- 相同的代码库,从 Mac Silicon 使用 MinGW 交叉编译到 Windows
<p>调查过程</p>
添加了详细的日志记录以跟踪执行情况。崩溃发生在成功解析约 6,000 行后进行字符串驻留时。不是在解析期间,也不是在文件 I/O 期间,而是在合并阶段。
<p>根本原因</p>
我的 StringPool 类使用了 `std::unordered_map<std::string_view, uint32_t>` 来去重字符串。`string_views` 指向一个 `std::vector<std::string>`。
当向量增长并重新分配时,所有的 `string_view` 键变成了悬空指针。哈希表中充满了无效引用。
为什么在 macOS 上可以正常工作?不同的内存分配器行为、不同的默认栈大小(8MB 对比 1MB)、不同的重新分配模式。
<p>修复方案</p>
修复前(崩溃):
```cpp
uint32_t intern(std::string_view str) {
auto it = indices_.find(str);
if (it != indices_.end()) return it->second;
uint32_t idx = strings_.size();
strings_.push_back(std::string(str));
indices_[std::string_view(strings_.back())] = idx; // 危险!
return idx;
}
```
修复后:
```cpp
uint32_t intern(const std::string& str) {
auto it = indices_.find(std::string_view(str));
if (it != indices_.end()) return it->second;
// 如果即将重新分配,提前重建
if (strings_.size() >= strings_.capacity()) {
strings_.reserve(strings_.capacity() * 2);
rebuildIndices(); // 修复所有 string_views!
}
uint32_t idx = strings_.size();
strings_.push_back(str);
indices_[std::string_view(strings_.back())] = idx;
return idx;
}
void rebuildIndices() {
indices_.clear();
for (size_t i = 0; i < strings_.size(); i++) {
indices_[std::string_view(strings_[i])] = i;
}
}
```
<p>结果</p>
- 100 万行:在 Windows 上耗时 6 秒
- 多个 GB 的文件:没有崩溃
- 吞吐量约为 166,000 行/秒
- 跨平台稳定性
<p>经验教训</p>
1. `std::string_view` 功能强大但危险 - 它是一个非拥有引用。当底层存储移动时,你持有的是垃圾。
2. 跨平台测试至关重要 - 由于不同的分配器行为和较大的默认栈大小,macOS 上的这个 bug 是不可见的。
3. 结构化日志优于调试器进行交叉编译 - 我是从 Mac 交叉编译到 Windows。将带时间戳的日志记录到文件中使崩溃点立即显而易见。
4. 小改动,大影响 - 一个函数,约 15 行代码,将“在 2MB 时崩溃”变成了“处理超过 5GB 的文件”。
5. 性能保持优秀 - 重建仅在向量重新分配(指数增长)期间发生,因此摊销成本可以忽略不计。
<p>技术栈</p>
- 使用 simdjson (v4.2.2) 进行解析
- 多线程解析(在我的测试机器上使用 20 个线程)
- 列式存储以提高内存效率
- C++17,使用 MinGW-w64 进行交叉编译
这让我深刻认识到,最关键的 bug 往往是最简单的,隐藏在平台差异的表面之下。
欢迎讨论实现细节、simdjson 的使用或跨平台 C++ 调试技术!