arrow技术初步学习

问题:mmap技术已经可以实现两个进程之间的零拷贝通信了,为什么还要再引入arrow?

mmap打通了两个进程之间的快速通信通道,但若仅依赖于mmap技术,依然存在很严重的性能问题。
举个例子,在Linux系统内存中(/dev/shm/),分别创建一个json文件和arrow文件,这两个文件的数据一样,都是100w行数据,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import json
import pyarrow as pa
import pyarrow.ipc as ipc
import numpy as np

NUM_ROWS = 1000000

# 创建一个100w行的数据
ids = np.arange(NUM_ROWS).tolist()
values = np.random.randn(NUM_ROWS).tolist()

# 在内存中创建一个json文件
JSON_FILE = '/dev/shm/test_data.json'
data_list = [{"id": i, "val": v} for i, v in zip(ids, values)]
with open(JSON_FILE, 'w') as f:
json.dump(data_list, f)

# 在内存中创建一个arrow文件
ARROW_FILE = '/dev/shm/test_data.arrow'
table = pa.Table.from_arrays([
pa.array(ids),
pa.array(values)
], names=['id', 'val'])
with pa.OSFile(ARROW_FILE, 'wb') as f:
with ipc.new_file(f, table.schema) as writer:
writer.write_table(table)

在文件创建完成之后,我们分别直接通过mmap映射方式去读取json文件和读取arrow文件,看看加载效率。

  • 将json文件通过mmap映射后,再通过json.loads加载解析,我们查看其耗时情况:
  • 将arrow文件通过pa.memory_map映射后,再通过ipc.open_file().read_all()加载解析,我们查看其耗时情况:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time
import os
import mmap

def benchmark():
# 加载json文件
start_json = time.time()
# 即使在内存中,JSON 也必须全量读取并解析
with open(JSON_FILE, 'r') as f:
# 问题:json.loads 必须扫描 100w 行字符串,并创建 100w 个 Python 字典对象
# 使用 json.loads 来解析 mmap 中的数据 因此:该步骤非常耗时
mapped_file = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
json_data = json.loads(mapped_file[:].decode("utf-8"))
end_json = time.time()
print(f"方案一 [JSON 解析] 耗时: {end_json - start_json:.4f} 秒")

# 加载arrow
start_arrow = time.time()
# 核心:使用 memory_map,这仅仅是建立一个逻辑映射,不发生 CPU 解析
source = pa.memory_map(ARROW_FILE, 'rb')
arrow_table = ipc.open_file(source).read_all()
end_arrow = time.time()
print(f"方案二 [Arrow 映射] 耗时: {end_arrow - start_arrow:.4f} 秒")

benchmark()

结果显示:

1
2
方案一 [JSON 解析] 耗时: 1.1515 秒
方案二 [Arrow 映射] 耗时: 0.0114 秒
文件 数据量 加载耗时(秒) 提升效率
JSON 100w行 1.1515 0
Arrow 100w行 0.0114 101倍

总结:
上面的例子说明,仅仅依靠mmap技术,进程可以快速获取到想要的数据;
但是问题是,获取到数据一般不是我们的最终目的,我们的目的一般是要理解这段数据并进行相关处理操作;因此在获取到数据之后,我们还需要对这段数据进行序列化成内存对象,以方便程序进行处理;
因此就有了如下区别:

  • mmap技术方便程序快速获取到json数据字节,接下来就需要程序自身将其转换成json对象,该阶段需要扫描字符、类型转换、编解码操作,这是一个CPU密集型操作,数据量越大就约耗时
  • 同样地,mmap技术方便程序快速获取到arrow数据,在加上arrow自身功能加持,程序读取到的arrow数据与在内存对象数据是一致的,无须进行解析操作,省去了CPU密集型的操作,因此加载数据时耗时几乎和数据量无关,因此数据量越大越能体现arrow的优势