为什么从文件中读取 1 个字节比读取 2、3、4、... 字节慢 20 倍?

2024-01-29

我一直试图理解之间的权衡read and seek。对于小的“跳跃”,读取不需要的数据比使用跳过它更快seek.

在计时不同的读取/查找块大小以找到临界点时,我遇到了一个奇怪的现象:read(1)大约慢20倍read(2), read(3)等。对于不同的读取方法,此效果是相同的,例如read() and readinto().

为什么会这样呢?

在计时结果中搜索以下第 2/3 行:

2 x buffered 1 byte readinto bytearray

环境:

Python 3.5.2 |Continuum Analytics, Inc.| (default, Jul  5 2016, 11:45:57) [MSC v.1900 32 bit (Intel)]

计时结果:

Non-cachable binary data ingestion (file object blk_size = 8192):
- 2 x buffered 0 byte readinto bytearray:
      robust mean: 6.01 µs +/- 377 ns
      min: 3.59 µs
- Buffered 0 byte seek followed by 0 byte readinto:
      robust mean: 9.31 µs +/- 506 ns
      min: 6.16 µs
- 2 x buffered 4 byte readinto bytearray:
      robust mean: 14.4 µs +/- 6.82 µs
      min: 2.57 µs
- 2 x buffered 7 byte readinto bytearray:
      robust mean: 14.5 µs +/- 6.76 µs
      min: 3.08 µs
- 2 x buffered 2 byte readinto bytearray:
      robust mean: 14.5 µs +/- 6.77 µs
      min: 3.08 µs
- 2 x buffered 5 byte readinto bytearray:
      robust mean: 14.5 µs +/- 6.76 µs
      min: 3.08 µs
- 2 x buffered 3 byte readinto bytearray:
      robust mean: 14.5 µs +/- 6.73 µs
      min: 2.57 µs
- 2 x buffered 49 byte readinto bytearray:
      robust mean: 14.5 µs +/- 6.72 µs
      min: 2.57 µs
- 2 x buffered 6 byte readinto bytearray:
      robust mean: 14.6 µs +/- 6.76 µs
      min: 3.08 µs
- 2 x buffered 343 byte readinto bytearray:
      robust mean: 15.3 µs +/- 6.43 µs
      min: 3.08 µs
- 2 x buffered 2401 byte readinto bytearray:
      robust mean: 138 µs +/- 247 µs
      min: 4.11 µs
- Buffered 7 byte seek followed by 7 byte readinto:
      robust mean: 278 µs +/- 333 µs
      min: 15.4 µs
- Buffered 3 byte seek followed by 3 byte readinto:
      robust mean: 279 µs +/- 333 µs
      min: 14.9 µs
- Buffered 1 byte seek followed by 1 byte readinto:
      robust mean: 279 µs +/- 334 µs
      min: 15.4 µs
- Buffered 2 byte seek followed by 2 byte readinto:
      robust mean: 279 µs +/- 334 µs
      min: 15.4 µs
- Buffered 4 byte seek followed by 4 byte readinto:
      robust mean: 279 µs +/- 334 µs
      min: 15.4 µs
- Buffered 49 byte seek followed by 49 byte readinto:
      robust mean: 281 µs +/- 336 µs
      min: 14.9 µs
- Buffered 6 byte seek followed by 6 byte readinto:
      robust mean: 281 µs +/- 337 µs
      min: 15.4 µs
- 2 x buffered 1 byte readinto bytearray:
      robust mean: 282 µs +/- 334 µs
      min: 17.5 µs
- Buffered 5 byte seek followed by 5 byte readinto:
      robust mean: 282 µs +/- 338 µs
      min: 15.4 µs
- Buffered 343 byte seek followed by 343 byte readinto:
      robust mean: 283 µs +/- 340 µs
      min: 15.4 µs
- Buffered 2401 byte seek followed by 2401 byte readinto:
      robust mean: 309 µs +/- 373 µs
      min: 15.4 µs
- Buffered 16807 byte seek followed by 16807 byte readinto:
      robust mean: 325 µs +/- 423 µs
      min: 15.4 µs
- 2 x buffered 16807 byte readinto bytearray:
      robust mean: 457 µs +/- 558 µs
      min: 16.9 µs
- Buffered 117649 byte seek followed by 117649 byte readinto:
      robust mean: 851 µs +/- 1.08 ms
      min: 15.9 µs
- 2 x buffered 117649 byte readinto bytearray:
      robust mean: 1.29 ms +/- 1.63 ms
      min: 18 µs

基准测试代码:

from _utils import BenchmarkResults

from timeit import timeit, repeat
import gc
import os
from contextlib import suppress
from math import floor
from random import randint

### Configuration

FILE_NAME = 'test.bin'
r = 5000
n = 100

reps = 1

chunk_sizes = list(range(7)) + [7**x for x in range(1,7)]

results = BenchmarkResults(description = 'Non-cachable binary data ingestion')


### Setup

FILE_SIZE = int(100e6)

# remove left over test file
with suppress(FileNotFoundError):
    os.unlink(FILE_NAME)

# determine how large a file needs to be to not fit in memory
gc.collect()
try:
    while True:
        data = bytearray(FILE_SIZE)
        del data
        FILE_SIZE *= 2
        gc.collect()
except MemoryError:
    FILE_SIZE *= 2
    print('Using file with {} GB'.format(FILE_SIZE / 1024**3))

# check enough data in file
required_size = sum(chunk_sizes)*2*2*reps*r
print('File size used: {} GB'.format(required_size / 1024**3))
assert required_size <= FILE_SIZE


# create test file
with open(FILE_NAME, 'wb') as file:
    buffer_size = int(10e6)
    data = bytearray(buffer_size)
    for i in range(int(FILE_SIZE / buffer_size)):
        file.write(data)

# read file once to try to force it into system cache as much as possible
from io import DEFAULT_BUFFER_SIZE
buffer_size = 10*DEFAULT_BUFFER_SIZE
buffer = bytearray(buffer_size)
with open(FILE_NAME, 'rb') as file:
    bytes_read = True
    while bytes_read:
        bytes_read = file.readinto(buffer)
    blk_size = file.raw._blksize

results.description += ' (file object blk_size = {})'.format(blk_size)

file = open(FILE_NAME, 'rb')

### Benchmarks

setup = \
"""
# random seek to avoid advantageous starting position biasing results
file.seek(randint(0, file.raw._blksize), 1)
"""

read_read = \
"""
file.read(chunk_size)
file.read(chunk_size)
"""

seek_seek = \
"""
file.seek(buffer_size, 1)
file.seek(buffer_size, 1)
"""

seek_read = \
"""
file.seek(buffer_size, 1)
file.read(chunk_size)
"""

read_read_timings = {}
seek_seek_timings = {}
seek_read_timings = {}
for chunk_size in chunk_sizes:
    read_read_timings[chunk_size] = []
    seek_seek_timings[chunk_size] = []
    seek_read_timings[chunk_size] = []

for j in range(r):
    #file.seek(0)
    for chunk_size in chunk_sizes:
        buffer = bytearray(chunk_size)
        read_read_timings[chunk_size].append(timeit(read_read, setup, number=reps, globals=globals()))
        #seek_seek_timings[chunk_size].append(timeit(seek_seek, setup, number=reps, globals=globals()))
        seek_read_timings[chunk_size].append(timeit(seek_read, setup, number=reps, globals=globals()))

for chunk_size in chunk_sizes:
    results['2 x buffered {} byte readinto bytearray'.format(chunk_size)] = read_read_timings[chunk_size]
    #results['2 x buffered {} byte seek'.format(chunk_size)] = seek_seek_timings[chunk_size]
    results['Buffered {} byte seek followed by {} byte readinto'.format(chunk_size, chunk_size)] = seek_read_timings[chunk_size]


### Cleanup
file.close()
os.unlink(FILE_NAME)

results.show()
results.save()

2020年2月24日编辑:

@finefoot 请求_utils包能够运行上面的代码。

from collections import OrderedDict
from math import ceil
from statistics import mean, stdev
from contextlib import suppress
import os
import inspect

class BenchmarkResults(OrderedDict):
    def __init__(self, *args, description='Benchmark Description', **kwArgs):
        self.description = description
        return super(BenchmarkResults, self).__init__(*args, **kwArgs)

    def __repr__(self):
        """Shows the results for the benchmarks in order of ascending duration"""
        characteristic_durations = []
        for name, timings in self.items():
            try:
                characteristic_durations.append(_robust_stats(timings)[0])
            except ValueError:
                if len(timings) > 1:
                    characteristic_durations.append(mean(timings))
                else:
                    characteristic_durations.append(timings[0])
        indx = _argsort(characteristic_durations)
        repr = '{}:\n'.format(self.description)
        items = list(self.items())
        for i in indx:
            name, timings = items[i]
            repr += '- {}:\n'.format(name)
            try:
                stats = _robust_stats(timings)
                repr += '      robust mean: {} +/- {}\n'.format(_units(stats[0]), _units(stats[1]))
            except ValueError:
                repr += '      timings: {}\n'.format(', '.join(map(_units, timings)))
            if len(timings) > 1:
                repr += '      min: {}\n'.format(_units(min(timings)))
        return repr

    def show(self):
        print(self)

    def save(self):
        caller = inspect.stack()[1]
        filename = os.path.splitext(caller.filename)[0] + '.log'
        with open(filename, 'w') as logfile:
            logfile.write(repr(self))


def _units(seconds, significant_figures=3):
    fmt = '{:.%sg} {}' % significant_figures
    if seconds > 1:
        return fmt.format(seconds, 's')
    elif seconds > 1e-3:
        return fmt.format(seconds*1e3, 'ms')
    elif seconds > 1e-6:
        return fmt.format(seconds*1e6, 'µs')
    elif seconds < 1e-6:
        return fmt.format(seconds*1e9, 'ns')
    elif seconds > 60:
        return fmt.format(seconds/60, 'min')
    else:
        return fmt.format(seconds/3600, 'hrs')
    raise ValueError()

def _robust_stats(timings, fraction_to_use=0.8):
    if len(timings) < 5:
        raise ValueError('To calculate a robust mean, you need at least 5 timing results')
    elts_to_prune = int(len(timings) * (1 - fraction_to_use))
    # prune at least the highest and the lowest result
    elts_to_prune = elts_to_prune if elts_to_prune > 2 else 2
    # round to even number --> symmetic pruning
    offset = ceil(elts_to_prune / 2)

    # sort the timings
    timings.sort()
    # prune the required fraction of the elements
    timings = timings[offset:-offset]
    return mean(timings), stdev(timings)

def _argsort(seq):
    # http://stackoverflow.com/questions/3071415/efficient-method-to-calculate-the-rank-vector-of-a-list-in-python
    return sorted(range(len(seq)), key=seq.__getitem__)

if __name__ == '__main__':
    pass

我能够用您的代码重现该问题。但是,我注意到以下内容:您能否验证如果更换则问题会消失

file.seek(randint(0, file.raw._blksize), 1)

with

file.seek(randint(0, file.raw._blksize), 0)

in setup?我认为在读取 1 个字节的过程中,您可能会在某个时刻耗尽数据。读取 2 个字节、3 个字节等不会有任何数据可读取,因此速度要快得多。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么从文件中读取 1 个字节比读取 2、3、4、... 字节慢 20 倍? 的相关文章

随机推荐

  • Webmatrix 布局——插入头部?

    我使用 Asp Net 和 Razor 视图引擎创建了一个网站 与使用 WebMatrix 相同 到目前为止 我一直在使用 SQL 直接查询数据库 但现在我有一个页面需要查询返回 XML 的 WCF 服务 让它返回 JSON 或 ODATA
  • 在 SQLAlchemy 中使用 declarative_base 时,如何根据需要绑定引擎?

    这是我的代码 from sqlalchemy import create engine Column Integer from sqlalchemy ext declarative import declarative base from
  • 如何在 postgresql 中找到列中的最大值?

    例如 name weight jon 100 jane 120 joe 130 如何只返回权重最大的人的名字 SELECT name FROM tbl ORDER BY weight DESC LIMIT 1 比其他答案性能更高 并且仅产生
  • 2013 年我应该使用 HTML5 编写我的网站吗? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 验证日期模式

    我需要一个 javascript 方法来验证日期模式 如下所示 2012 年 1 月 14 日 到目前为止 我不确定该模式使用哪个正则表达式 因为它不是标准日期 例如 14 01 2012 或 01 14 2012 或 01 04 2012
  • 为什么 jqueryUI datepicker 会抛出错误?

    我正在尝试 jqueryUI 但 firebug 在该脚本上捕获以下错误 function date datepicker 萤火虫错误如下 date datepicker is not a function 在我的 html 上 日期 id
  • 启发式和元启发式有什么区别?

    经过对算法的一些研究后 我发现了两个令我困惑的术语 我读过至少 20 篇论文 但都没有任何明确的定义 我希望有人能帮助我区分启发式算法和元启发式算法之间的区别 如果可能的话 添加它的来源 ps 我已经知道这些词的含义 但我不知道它们在计算机
  • 如何将旋转器数据从一个活动传递到另一个活动?

    此代码不会从微调器读取值 它始终只读取第一个值 btnResult setOnClickListener new View OnClickListener final String USN spnConversions getSelecte
  • 在 Visual Studio 中设置 FILE_ATTRIBUTE_DEVICE

    我想知道是否可以在 Visual C 中调用 CreateFile 函数来创建属性为 FILE ATTRIBUTE DEVICE 0x00000040 十六进制 64 十进制 的文件 根据 MSDN API FILE ATTRIBUTE D
  • Laravel 4 - 使用 hasMany 关系时插入多条记录

    仍在适应 Laravel 4 我有点不确定为什么这不起作用 在 L3 中 我能够将多条记录插入到表中 如下所示 comments array array message gt A new comment array message gt A
  • 依赖项规范必须是字符串或映射

    pubspec yaml 第 32 行第 5 列出错 依赖项规范必须是字符串或映射 32 assets 33 酒吧失败 65 问题是我的资产文件夹位于错误的位置 它不应该处于依赖关系下 另外 assets 前必须加2个空格 如图
  • Swift 中的属性和变量有什​​么区别?

    从一些最初的教程中 我看到属性属于一个类 本质上是 C 世界中使用的 全局变量 几年前编码 我还认为变量更多的是仅在方法中使用 存储信息的 本地 实体 然后我看到了 Quora 的这个帖子 https www quora com Apple
  • Java win32 库/api

    是否有合适的 Java win32 库 例如 显示当前进程 查找进程占用的端口号等 或者像 WMI 库之类的东西 看一下JNA https github com twall jna 这是与本机代码通信的 100 纯 java 方式 他们有一
  • 如何抑制 Delphi DataSnap 错误消息对话框?

    我们在 Windows 2003 Server 上运行 DataSnap Delphi 2009 应用程序 DataSnap 客户端和服务器位于同一台计算机上 通过 Borland Socketserver 使用 DCOM 客户端运行后台批
  • 为什么将 Avro 与 Kafka 结合使用 - 如何处理 POJO

    我有一个 Spring 应用程序 它是我的 kafka 生产者 我想知道为什么 avro 是最好的选择 我读到了它以及它提供的所有内容 但为什么我不能序列化我用 jackson 自己创建的 POJO 并将其发送到 kafka 我这样说是因为
  • Amazon MWS Feed API 更新订单状态时出现问题

    我正在使用 amazon mws feed api 来更新我网站上的订单状态 当我打电话给提交供稿api 提交成功 但是 当我打电话给获取Feed提交结果 它返回给我一个错误 指出
  • 使用 UITextView 和 NSMutableAttributedString 对齐文本

    我正在尝试为一个合理的文本UITextView with NSMutableAttributedString the NSMutableAttributedString是由不同的NSAttributedString因为我需要粗体和常规字体
  • 什么是 Chocolatey“安装”包?

    关于审查巧克力包装 https chocolatey org packages可用时 我遇到了一些有两个 或有时更多 显然用于同一产品的软件包 乍一看无法区分 例如 有自动热键包 然后还有一个自动热键 install 在这里查看 Choco
  • 为什么 JavaScript 中 (([]===[])+/-/)[1] = 'a' 和 (1+{})[(1<<1)+1] = 'b' ?

    最近我发现了一个有趣的网站 其中展示了 Javascript 混淆器 http bl ocks org jasonsperske 5400283 http bl ocks org jasonsperske 5400283 例如 1 give
  • 为什么从文件中读取 1 个字节比读取 2、3、4、... 字节慢 20 倍?

    我一直试图理解之间的权衡read and seek 对于小的 跳跃 读取不需要的数据比使用跳过它更快seek 在计时不同的读取 查找块大小以找到临界点时 我遇到了一个奇怪的现象 read 1 大约慢20倍read 2 read 3 等 对于