我正在通过LazyDataFrame API polars.scan_csvfilter测试polars的性能.演出比我预期的好得多.过滤CSV文件甚至比磁盘速度更快!为什么?

CSV文件在我的电脑硬盘上大约有1.51 GB个.

测试代码:

import polars as pl
t0 = time.time()
lazy_df = pl.scan_csv("kline.csv")
df = lazy_df.filter(pl.col('ts') == '2015-01-01').collect().to_pandas()
print(time.time() - t0)

> Output: 1.8616907596588135

扫描整个CSV文件所需的时间不到2 seconds,这意味着扫描速度快于750MB/S.显然,它比磁盘速度快得多.

推荐答案

您可能看到的是基准测试中的一个常见问题:the caching of files by your operating system.如果内存量允许,大多数现代操作系统都会try 缓存访问的文件.

第一次访问该文件时,您的操作系统可能会将1.51 GB的文件缓存在RAM中(甚至可能在您创建该文件时).因此,后续的检索并不是真正访问硬盘——它们是针对RAM中的缓存文件运行的,这一过程比从硬盘读取要快得多.(这是在RAM中缓存文件的一种方式.)

An Example

例如,我创建了一个29.9 GB的csv文件,并有意将其放置在我的NAS(网络连接存储)上,而不是本地硬盘上.作为参考,我的NAS和我的机器通过10千兆/秒的网络连接.

第一次运行这个基准测试代码大约需要54秒.

import polars as pl
import time
start = time.perf_counter()
(
    pl.scan_csv('/mnt/bak-projects/StackOverflow/benchmark.csv')
    .filter(pl.col('col_0') == 100)
    .collect()
)
print(time.perf_counter() - start)
shape: (1, 27)
┌─────┬───────┬───────┬───────┬─────┬────────┬────────┬────────┬────────┐
│ id  ┆ col_0 ┆ col_1 ┆ col_2 ┆ ... ┆ col_22 ┆ col_23 ┆ col_24 ┆ col_25 │
│ --- ┆ ---   ┆ ---   ┆ ---   ┆     ┆ ---    ┆ ---    ┆ ---    ┆ ---    │
│ f64 ┆ i64   ┆ i64   ┆ i64   ┆     ┆ i64    ┆ i64    ┆ i64    ┆ i64    │
╞═════╪═══════╪═══════╪═══════╪═════╪════════╪════════╪════════╪════════╡
│ 1.0 ┆ 100   ┆ 100   ┆ 100   ┆ ... ┆ 100    ┆ 100    ┆ 100    ┆ 100    │
└─────┴───────┴───────┴───────┴─────┴────────┴────────┴────────┴────────┘
>>> print(time.perf_counter() - start)
53.92608916899917

因此,在54秒内读取一个29.9 GB的文件大约是29.9 GB*(每字节8位)/54秒=4.4 GB/秒.从网络驱动器检索文件不错.当然,在我的万兆/秒网络上是可能的.

然而,文件现在被我的操作系统(Linux)缓存在RAM中(我有512 GB的RAM).因此,当我第二次运行相同的基准测试代码时,只花了3.5秒:

shape: (1, 27)
┌─────┬───────┬───────┬───────┬─────┬────────┬────────┬────────┬────────┐
│ id  ┆ col_0 ┆ col_1 ┆ col_2 ┆ ... ┆ col_22 ┆ col_23 ┆ col_24 ┆ col_25 │
│ --- ┆ ---   ┆ ---   ┆ ---   ┆     ┆ ---    ┆ ---    ┆ ---    ┆ ---    │
│ f64 ┆ i64   ┆ i64   ┆ i64   ┆     ┆ i64    ┆ i64    ┆ i64    ┆ i64    │
╞═════╪═══════╪═══════╪═══════╪═════╪════════╪════════╪════════╪════════╡
│ 1.0 ┆ 100   ┆ 100   ┆ 100   ┆ ... ┆ 100    ┆ 100    ┆ 100    ┆ 100    │
└─────┴───────┴───────┴───────┴─────┴────────┴────────┴────────┴────────┘
>>> print(time.perf_counter() - start)
3.5459880090020306

如果我的29.9 GB文件真的在网络上传输,这意味着网络速度至少为29.9*8/3.5秒=68 GB/s.(显然,在我的万兆/秒网络上是不可能的.)

第三次:2.9秒

shape: (1, 27)
┌─────┬───────┬───────┬───────┬─────┬────────┬────────┬────────┬────────┐
│ id  ┆ col_0 ┆ col_1 ┆ col_2 ┆ ... ┆ col_22 ┆ col_23 ┆ col_24 ┆ col_25 │
│ --- ┆ ---   ┆ ---   ┆ ---   ┆     ┆ ---    ┆ ---    ┆ ---    ┆ ---    │
│ f64 ┆ i64   ┆ i64   ┆ i64   ┆     ┆ i64    ┆ i64    ┆ i64    ┆ i64    │
╞═════╪═══════╪═══════╪═══════╪═════╪════════╪════════╪════════╪════════╡
│ 1.0 ┆ 100   ┆ 100   ┆ 100   ┆ ... ┆ 100    ┆ 100    ┆ 100    ┆ 100    │
└─────┴───────┴───────┴───────┴─────┴────────┴────────┴────────┴────────┘
>>> print(time.perf_counter() - start)
2.8593162479992316

根据您的操作系统,有一种方法可以在基准测试之前从RAM中刷新缓存文件.

Python相关问答推荐

Pandas或pyspark跨越列创建

为什么图像结果翻转了90度?

如何分割我的收件箱,以便连续的数字各自位于自己的收件箱中?

Python如何让代码在一个程序中工作而不在其他程序中工作

使用imap-tools时错误,其邮箱地址包含域名中的非默认字符

用Python获取HTML Span类中的数据

Pydantic:如何将对象列表表示为dict(将列表序列化为dict)

在上下文管理器中更改异常类型

时间序列分解

试图找到Python方法来部分填充numpy数组

沿着数组中的轴计算真实条目

在Wayland上使用setCellWidget时,try 编辑QTable Widget中的单元格时,PyQt 6崩溃

Python中绕y轴曲线的旋转

在Python中动态计算范围

NumPy中条件嵌套for循环的向量化

cv2.matchTemplate函数匹配失败

pandas在第1列的id,第2列的标题,第3列的值,第3列的值?

如何在Python中找到线性依赖mod 2

使用Python从URL下载Excel文件

如何从需要点击/切换的网页中提取表格?