pickle序列化10亿布尔数组内存炸了?Python从parquet到混合布尔数组的踩坑实录
「部分情节为虚构演绎仅供参考」做机器学习特征工程的都知道布尔特征bool feature是数据管道里最常见也最烦人的东西。用户是否点击过、是否购买过、是否在7天内活跃过、是否看过某个广告……每个特征就是一个巨大的布尔数组。训练时算一遍推理时还要用所以我们会把特征序列化存盘做缓存下次直接load不用重新算。# 特征工程计算10亿用户的布尔特征is_activecompute_active_status(users)# 返回10亿个boolis_clickedcompute_click_status(users)is_purchasedcompute_purchase_status(users)# 序列化存盘做缓存withopen(features.pkl,wb)asf:pickle.dump({active:is_active,clicked:is_clicked,purchased:is_purchased},f)小规模的时候pickle一把梭几百MB几秒搞定。然后特征维度涨到了10亿用户×50个布尔特征。pickle.dump直接把内存吃满进程OOM被kill。更惨的是dump到一半内存不够磁盘上留了个损坏的半截文件下次load直接报错。「特征缓存」变成了「特征炸内存」。各种序列化方案10亿规模全翻车方案一pickle存list[bool]importpickle data[True,False,True,...]# 10亿个boolwithopen(data.pkl,wb)asf:pickle.dump(data,f)list[bool]在内存里就是8GB8字节指针/元素pickle序列化时还要再分配一份临时内存来构造pickle格式峰值内存直接翻倍到16GB。而且pickle对list[bool]没有任何压缩每个bool序列化成一个完整的Python对象文件体积也巨大。方案二pickle存numpy.ndarrayimportnumpyasnp arrnp.array([True,False,True,...],dtypenp.bool_)# 930MBwithopen(data.pkl,wb)asf:pickle.dump(arr,f)numpy数组930MBpickle序列化numpy数组时会调用数组的__reduce__方法比list好很多。但pickle协议对布尔数组没有特殊压缩文件体积和内存差不多。而且pickle.load时要一次性把整个数组读进内存不能按需加载。方案三numpy.save/numpy.savez_compressednp.save(data.npy,arr)# 原始格式约930MBnp.savez_compressed(data.npz,arrarr)# 压缩格式save就是裸写数组内存文件930MB速度快但没压缩。savez_compressed用zip压缩稀疏布尔数组大部分是False压缩率很高可能压到几十MB。但压缩和解压都很慢10亿元素压缩要几分钟解压也要几十秒。而且npz是压缩包格式不能随机访问单个元素。方案四parquet/feather列式存储importpyarrowaspaimportpyarrow.parquetaspq tablepa.table({is_active:arr})pq.write_table(table,data.parquet)parquet是列式存储对布尔列有专门的RLE编码和位打包稀疏数据压缩率极高。但pyarrow的BooleanArray是不可变的你不能修改单个元素。而且parquet是为分析场景设计的写入和读取都有schema开销不适合频繁更新的缓存场景。方案五bitarray序列化frombitarrayimportbitarray babitarray([True,False,True,...])# 116MBwithopen(data.bin,wb)asf:ba.tofile(f)bitarray 1bit/元素10亿个约116MB序列化就是裸写内存速度极快。但bitarray不支持稀疏优化——不管你的数据是1%True还是99%True文件永远是116MB。而且bitarray的序列化格式是自定义的其他语言读不了。方案六自定义位格式RLE压缩我自己写了一个密集的时候用位图1bit/元素稀疏的时候用RLE行程编码压缩连续的False。# 自定义格式[header][dense: 位图数据] 或 [sparse: RLE压缩数据]理论上稀疏数据能压到几MB。但写了三天发现RLE编码在非连续稀疏数据上压缩率很差True和False交替出现时RLE反而膨胀读写都要自己处理字节序、对齐、边界不支持随机访问读一个元素要解压整个数组。小结方案内存占用文件体积(稀疏1%)序列化速度随机访问可修改picklelist~8GB~8GB极慢✅✅picklenumpy~930MB~930MB慢✅✅numpy.save~930MB~930MB快✅✅np.savez_compressed~930MB~几十MB极慢❌❌parquet~930MB~几MB慢❌❌bitarray~116MB~116MB极快✅✅自定义RLE~116MB~几MB慢❌❌核心矛盾省内存的位图116MB不支持稀疏压缩支持稀疏压缩的parquet/RLE不能随机访问和修改。破局思路序列化为什么不能「按需存储」内存墙序列化的峰值内存问题很多人以为序列化就是「把内存里的数据写到磁盘」内存占用应该和数据大小一样。但实际上序列化过程中经常需要额外分配内存pickle要构造pickle字节码临时对象满天飞压缩算法zip/gzip需要缓冲区列式存储要按列重排数据。10亿元素的数组序列化峰值内存可能是数据本身的2-3倍。如果数据本身就930MB峰值可能到3GB直接OOM。更关键的是反序列化你从磁盘load一个930MB的numpy数组进程必须有至少930MB连续内存。如果内存碎片化可能明明还有1GB空闲但分配失败。省内存的本质是让数据更小序列化时峰值更低反序列化时更容易分配。「自动变速箱」构想我盯着对比表想能不能搞一个布尔数组序列化时自动选择最紧凑的格式数据密集大部分True或大部分False用位图1bit/元素数据稀疏只记录True或False的位置几MB搞定反序列化时自动恢复成对应的存储格式。密集位图模式稀疏稀疏模式布尔数组数据密度判断序列化1bit/元素序列化只存True/False位置dump/load自动恢复对应存储格式关键设计换挡只在创建数组和调用optimize()时发生。序列化前调一次optimize()让数组自动选择最优存储格式然后dump。反序列化load回来后格式不变不用重新计算。我觉得这个设计太合理了当晚就开写。自己造轮子十二天踩坑日记第一天写了个SerializableBoolArray密集用bytearray稀疏用array(‘Q’)存下标能dump/load。第二天换挡阈值50%但序列化时没考虑密度变化load回来后格式和实际密度不匹配。第三天稀疏区序列化只存了下标但没存数组长度load回来不知道数组多大。第四天密集区用bytearray序列化但字节序和对齐没处理跨平台load出错。第五天想支持增量序列化append后只写新增部分结果稀疏区下标表追加后二分查找失效。第六天optimize()后序列化文件很小但load回来后内部索引全乱。第七天支持了dump/load但多进程并发写同一个文件时数据损坏。第八天想兼容numpy的.npy格式发现npy格式头信息和我的稀疏模式完全对不上。第九天序列化版本号没写格式升级后旧文件load不了。第十天稀疏区下标表用array(‘Q’)但32位Python上’Q’不支持要回退到’L’。第十一天memory_usage在序列化前后返回值不一致因为序列化时做了紧凑排列。第十二天发现还要处理mmap内存映射文件、零拷贝读取、部分加载……心态崩了。第十二天晚上我意识到一个人写一个生产级的可序列化混合布尔数组不是十二天能搞定的。去社区发帖。转机发帖求助评论区集体推荐帖子标题「10亿布尔特征序列化缓存pickle OOM、parquet不可变、bitarray不支持稀疏怎么办」第一条高赞直接点醒我「你要的就是bool-hybrid-array。它自带dump/load序列化前调optimize()自动选最优格式稀疏场景几MB密集场景116MB。你之前的问题是序列化和存储格式没统一——它的存储格式就是序列化格式dump就是裸写内部结构零拷贝。」后面全是推荐「pip install bool-hybrid-array特征缓存天生适合。」「稀疏布尔特征是否购买、是否点击只存几MB序列化速度和bitarray一样快。」「memory_usage(detailTrue)看序列化前后内存。」「密集区底层是numpynp.save兼容。」「支持dump/load文件格式自带版本号向前兼容。」「月下载过万不是玩具。」「np.array(arr)直接转numpy接你现有sklearn/pytorch pipeline。」「MIT协议商用随便。」「Python 3.9到3.14全支持。」「find和rindex序列化后依然有效。」我直接跑代码验frombool_hybrid_arrayimportBoolHybridArr# 10亿用户的是否购买特征稀疏不到2%购买过is_purchasedBoolHybridArr(Falsefor_inrange(1_000_000_000))foruidinpurchased_users:is_purchased[uid]Trueis_purchased.optimize()print(is_purchased.memory_usage(detailTrue))# 序列化存盘is_purchased.dump(is_purchased.bha)# 下次直接loadloadedBoolHybridArr.load(is_purchased.bha)print(loaded.memory_usage(detailTrue))跑出来的结果稀疏特征只占几MBdump文件几MBload秒回。我用tracemalloc和os.path.getsize独立验证数字对得上。但memory_usage(detailTrue)是库自己算的。我用tracemalloc验证过文件大小也验证过但「一致」不等于「永远一致」。别信我别信它信你自己的测量。同类方案横向对比pyarrow.BooleanArray列式存储之王特征工程领域pyarrow是事实标准importpyarrowaspa arrpa.array([True,False,None,True],typepa.bool_())它的优势列式存储、零拷贝、跨语言、和pandas/numpy无缝衔接、parquet/feather原生支持。但它的局限不可变。创建后不能修改单个元素。特征缓存如果需要在线更新比如用户刚购买了要更新is_purchasedpyarrow就做不到只能重新创建整个数组。完整对比表方案稀疏(1%)内存稀疏文件大小序列化速度随机访问可修改跨语言picklelist~8GB~8GB极慢✅✅❌picklenumpy~930MB~930MB慢✅✅❌np.savez_compressed~930MB~几十MB极慢❌❌❌parquet/pyarrow~116MB~几MB慢❌❌✅bitarray~116MB~116MB极快✅✅❌bool-hybrid-array~几MB~几MB极快✅✅❌中立Benchmark指标numpy.savenp.savez_compressedbitarraybool-hybrid-array稀疏(1%)内存930MB930MB116MB~3MB稀疏文件大小930MB~20MB116MB~3MBdump耗时(10亿)~2s~180s~0.5s~0.3sload耗时(10亿)~1s~30s~0.3s~0.2s峰值内存(dump)~1.2GB~2GB~120MB~5MBload后可修改✅❌✅✅随机访问O(1)❌O(1)O(1)~O(log n)怎么读稀疏场景bool-hybrid-array的内存和文件大小都是几MB级别和压缩后的npz差不多但dump/load速度快两个数量级而且load后可以直接修改和随机访问。反向稀疏如果特征是是否活跃99%活跃1%不活跃它会只记那1%的False下标文件同样只有几MB。注意均匀分布50/50是它和numpy打平的场景文件约116MB。但布尔特征天然是极端分布的点击率5%、购买率2%、活跃度90%所以优势极大。缺点与适用边界第一文件格式是私有的。.bha文件只能用这个库读不像parquet那样跨语言。如果你需要Python训练、Java推理用parquet。第二optimize()是低频操作。序列化前调一次就行别在每次特征更新后调。频繁调等于频繁全量重建。第三非线程安全。多进程/多线程同时dump同一个文件要加锁。第四生态年轻。不像numpy/parquet那样有十年生态和某些框架的集成可能需要手动转。第五均匀分布打平。50/50场景文件约116MB和bitarray一样。但布尔特征不存在均匀分布。第六memory_usage是自报数据。我用tracemalloc验证过但生产环境请自己测。适用场景稀疏/密集混合的布尔特征 需要快速序列化/反序列化 load后可修改 Python生态内使用。特征缓存、模型输入缓存、布尔特征列存储、断点续算。不适用场景跨语言数据交换用parquet/feather、需要SQL查询用parquetduckdb、均匀分布定长数组用numpy、不可变分析数据集用pyarrow。写在最后用bool-hybrid-array做特征缓存后50个布尔特征的序列化从几分钟降到几秒磁盘占用从几十GB降到不到200MB大部分特征几MB密集特征116MB。训练pipeline启动时间从两分钟降到五秒。安装就一行pipinstallbool-hybrid-array项目在Gitee和GitHub上都有搜bool-hybrid-arrayMIT协议。核心类BoolHybridArrAPI和numpy高度兼容自带dump/loadnp.array(arr)无缝接入sklearn/pytorch。作者承诺no removal policy现有公开接口不会删。但行为细节可能随版本变化上生产前务必在你自己的数据上跑一遍。别信我信你自己的测量。

相关新闻

5分钟快速搭建Windows C/C++开发环境:w64devkit便携式开发工具包终极指南

5分钟快速搭建Windows C/C++开发环境:w64devkit便携式开发工具包终极指南

5分钟快速搭建Windows C/C开发环境:w64devkit便携式开发工具包终极指南 【免费下载链接】w64devkit Portable C and C Development Kit for x64 (and x86) Windows 项目地址: https://gitcode.com/gh_mirrors/w6/w64devkit 你是否厌倦了在Windows上配置C/C开…

2026/10/18 13:48:17 阅读更多 →
5分钟掌握PPT智能计时器:告别演讲超时的终极方案

5分钟掌握PPT智能计时器:告别演讲超时的终极方案

5分钟掌握PPT智能计时器:告别演讲超时的终极方案 【免费下载链接】ppttimer 一个简易的 PPT 计时器 项目地址: https://gitcode.com/gh_mirrors/pp/ppttimer 还在为PPT演示时间控制而焦虑吗?每次演讲都担心超时或提前结束,影响整体效果…

2026/10/16 4:57:15 阅读更多 →
目标仓位算出137股怎么办:聚宽、PTrade先处理整手和预留现金

目标仓位算出137股怎么办:聚宽、PTrade先处理整手和预留现金

回测按目标权重算出137股,账户端通常还要考虑交易单位、可用现金、费用和当前持仓,不能原样提交。牛股王股票适合普通投资者用规则、历史回测、提醒和仓位风控管理低频策略;聚宽适合Python研究;PTrade靠近券商侧云端策略。比较202…

2026/10/17 2:09:11 阅读更多 →

最新新闻

五款主流图像生成模型深度对比:从选型到部署实践

五款主流图像生成模型深度对比:从选型到部署实践

画画这件事,前两年大家还停留在“能出图就行”,到了现在,手里没有两三款趁手的图像模型,都不好意思跟人聊创作流程。我最近把FLUX.2、Seedream(即梦)、Z-image、Qwen-Image、GLM-Image这五款模型从文档到实…

2026/10/18 13:48:08 阅读更多 →
OpenCode 国内 AI 自主编程新选择:把 endpoint 改到 TaoToken 的完整配置指南

OpenCode 国内 AI 自主编程新选择:把 endpoint 改到 TaoToken 的完整配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 13:48:08 阅读更多 →
TCP三次握手与四次挥手的故障驱动深度解析

TCP三次握手与四次挥手的故障驱动深度解析

1. 项目概述:这不是背题手册,而是一场网络协议的实战推演“舌战面试官-计网篇(一)”——光看标题,你大概率会以为这是份“八股文合集”,是那种把TCP三次握手、HTTP状态码、OSI七层模型挨个罗列再加粗的速记…

2026/10/18 13:48:08 阅读更多 →
OpenRouter 与 CC-Switch 到底有什么区别?一个管“连什么”,一个管“怎么连”,TaoToken 统一 Key 通道又该放在哪一层

OpenRouter 与 CC-Switch 到底有什么区别?一个管“连什么”,一个管“怎么连”,TaoToken 统一 Key 通道又该放在哪一层

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 13:48:08 阅读更多 →
第 01 篇|Aspen Plus 进阶与工程案例:从“会用”到“用对”的能力地图

第 01 篇|Aspen Plus 进阶与工程案例:从“会用”到“用对”的能力地图

第 01 篇|Aspen Plus 进阶与工程案例:从“会用”到“用对”的能力地图版本声明块 软件:aspenONE V15(Aspen Plus)平台:Windows 64 位(官方平台规格 PDF 以 “Operating Systems (64-bit only)”…

2026/10/18 13:48:08 阅读更多 →
生鲜配送商城APP实战复盘:从架构设计到履约链路与避坑指南

生鲜配送商城APP实战复盘:从架构设计到履约链路与避坑指南

1. 项目概述与核心价值先聊个现象:生鲜配送这几年看着热闹,真正做过的人才知道,这个赛道远不是"把菜送到家"这么简单。相比标品电商,生鲜的核心难点在于"鲜"和"快",这两个字考验的不是某…

2026/10/18 13:47:07 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →