减少Python String内存在Apache arrow 0.12中使用

2021-06-13 13:53:19

升级到最近发布的Pyarrow 0.12的Python用户可能会发现在将箭头字符串转换为Pandas格式时,该应用程序使用显着较少的内存。这包括使用pyarrow.parquet.read_table和pandas.read_parquet。本文详细介绍了以下内容的一些情况,以及为什么Python应用程序处理大量字符串以上是内存使用问题。

让我们从一些可能的令人惊讶的事实开始。我将在Python 3.7中创建一个空字节对象和一个空的str(Unicode)对象:

sys.getsizeof函数准确地报告Bython对象中使用的字节数。你可能会发现:

由于Python中的字符串终止,因此我们可以推断一个字节objecthas 32字节的开销,而Unicode有48字节。还必须解释PyObject *指针对对象的引用,因此实际开销分别为40和56字节。具有大字符串和文本,此开销可能会很多,但是当您有很多小字符串时,例如读取CSV或Apache Parquet文件的那些引发的小字符串时,它们可以占用内部内部内部。 Pandas表示PyObject *指针的Numpy数组中的字符串,因此唯一Unicode字符串使用的总内存是

在磁盘上,此文件大约为10MB。然而,读入内存,它可以占用超过60MB,因为一个10个字符的字符串对象占用Pandas.Series中的67个字节。

虽然Python Unicode字符串可以具有57个字节的开销,但是柱状格式中的字符串只有4(32位)或4.125(33位)字节。 32位整数偏移重启将StringValue的位置和大小进行编码在内存连续块中:

使用Pyarrow调用table.to_pandas()或array.to_pandas(),我们将此紧凑的字符串表示转换为基于Pandas'Spython的字符串。当我们有一个小字符串的Largenumber时,这可以使用大量的记忆。在使用WebAnalytics数据时,它是一个非常常见的发生,该数据在存储在ParqueTColumnar文件格式中时压缩到紧凑的尺寸。

请注意,箭头字符串内存格式具有超出模拟的其他优势。由于Datalocality的保证,分析也更有效;所有字符串都在内存中彼此相邻。在Pandasand Python字符串的情况下,字符串数据可以位于ProcessHeap中的任何位置。箭头PMC成员UWE Korn确实有效地扩展了箭头阵列的Pandas,以改善性能和内存使用。

多年来,Pandas.Read_CSV函数依赖于一个限制分配的字符串内存量的技巧。因为熊猫使用pyObject *指针的数组来引用Python堆中的对象,我们可以避免具有相同值的多个字符串,而是重用现有的对象和递增其参考计数。

在Pyarrow 0.12中,我们在调用to_pandas时实现了这一点。使用哈希表将箭头字符串数据重复删除箭头字符串,因为它将箭头字符串数据呈现给熊猫。散列数据不是免费的,但违反常见案例在常用案例中的内存高效的情况下,我们可以避免使用桌子列,其中有许多相同的StringValues实例。

我们可以使用Memory_Profiler Python软件包轻松获取正在运行的Python应用程序中的ProcessMemory使用。

我将生成近似1千兆字节的字符串数据,表示为具有长度10的PythonStrings .Pandas.util.Testing模块具有用于生成随机字符串的方便randsfunction。以下是数据生成功能:

来自Pandas.util.Testing Import Rands Def Generate_Strings(长度,nunique,string_length = 10):unique_values = [rand(string_length)In Range(nunique)]值= unique_values *(length // nunique)返回值

这会生成一定数量的唯一字符串,然后重复,然后重复签字席,所需的总字符串数量。所以我打算创造1000百万个独特的价值观:

1亿pyObject *值仅为745 MB,因此,这一幼点的增加770 MB是与我们到目前为止所知道的。现在我要转换为arrow格式:

由于每个字符串大约需要14个字节(10个字节加上4个字节的开销),因此这就是我们所预期的。

现在,将Arr恢复到熊猫是事情的棘手。 Pandas可以使用的Memory的最小值是如上800 MB的略低于800 MB,如Weneed 100万pyObject *值,每个值为8字节。

做数学,我们使用了765 MB,似乎是正确的。我们可以通过将deduplate_objects = false传递给to_pandas来禁用stringdeduplication逻辑:

没有对象重复数据删除,我们使用6965兆字节,或平均73个字节值。这比上面的67字节的理论尺寸高一点。

更令人惊讶的结果是,新的行为是快速的两倍:

在[18]中:%time arr_as_pandas_time = arr.to_pandas()cpu次数:用户2.94 s,sys:213 ms,总计:3.15势速时间:3.14 sin [19]:%time arr_as_pandas_no_dedup_time = arr.to_pandas(deduplate_objects = false) CPU次数:用户4.19 S,SYS:2.04 S,总计:6.23速度时间:6.21 s

为此的原因是创建如此多的Python对象更加expensivehan散列10字节值并在哈希表中查找它们。 请注意,当您将箭头数据转换为大多数唯一值返回Pandas时,这里的内存使用福利将不会产生任何影响。 在Apache arrow中,我们的目标是开发计算工具,以便自然运行缓存和SIMD友好的高效箭头柱状格式。 但是,在主题中,我们认识到用户使用Pandas或其他分析工具的Hyative Memory布局具有遗留应用程序。 我们将用熊猫和其他物品库进行最佳的快速和内存高效的互操作性。