gpt4 book ai didi

python-3.x - Python3的开放缓冲参数看起来很奇怪

转载 作者:行者123 更新时间:2023-12-03 16:27:36 24 4
gpt4 key购买 nike

doc


buffering是一个可选的整数,用于设置缓冲策略。
传递0以关闭缓冲(仅在二进制模式下允许),传递1至
选择行缓冲(仅在文本模式下可用),并且整数> 1
指示固定大小的块缓冲区的大小(以字节为单位)。当没有
给定buffering参数,默认的缓冲策略为
如下:

二进制文件以固定大小的块缓冲;使用试探法来确定缓冲区的大小来选择缓冲区的大小
设备的“块大小”,然后返回io.DEFAULT_BUFFER_SIZE。上
在许多系统中,缓冲区的长度通常为4096或8192字节。
“交互式”文本文件(isatty()返回True的文件)使用行缓冲。其他文本文件使用上述策略
用于二进制文件。


我以文本模式打开一个名为test.log的文件,并将缓冲设置为16。因此,我认为块大小为16,并且在向该文件写入32个字节的字符串时。它将两次调用write(syscall)。但实际上,它只调用一次(在Linux 3.7.2 GCC 8.2.1 20181127上进行测试)

import os


try:
os.unlink('test.log')
except Exception:
pass


with open('test.log', 'a', buffering=16) as f:
for _ in range(10):
f.write('a' * 32)



使用 strace -e write python3 test.py跟踪syscall,并获得以下信息

write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 320) = 320


buffering是什么意思?

最佳答案

这个答案对CPython 3.7有效,其他Python实现可能有所不同。

文本模式下的open()函数返回_io.TextIOWrapper()_io.TextIOWrapper()具有称为“ pending_bytes”的内部“缓冲区”,其大小为8192字节(它是hard coded),并且在_io.BufferedWriter()上对于文本模式w或在_io.BufferedRandom()上对于文本模式a也具有句柄。 _io.BufferedWriter() / _io.BufferedRandom()的大小由buffering函数中的参数open()指定。

调用_io.TextIOWrapper().write("some text")时,它将文本添加到内部pending_bytes缓冲区中。写入后,将填充pending_bytes缓冲区,然后将其写入_io.BufferedWriter()内部的缓冲区。当您也填满_io.BufferedWriter()内部的缓冲区时,它将被写入目标文件。

当您以二进制模式打开文件时,您将直接获得_io.BufferedWriter() / _io.BufferedRandom()对象,该对象使用来自buffering参数的缓冲区大小进行了初始化。

让我们看一些例子。我将从使用二进制模式的简单示例开始。

# Case 1
with open('test.log', 'wb', buffering=16) as f:
for _ in range(5):
f.write(b'a'*15)


strace输出:

write(3, "aaaaaaaaaaaaaaa", 15)         = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15
write(3, "aaaaaaaaaaaaaaa", 15) = 15


在第一次迭代中,它用15个字节填充缓冲区。在第二次迭代中,它发现再添加15个字节将使缓冲区溢出,因此首先刷新缓冲区(调用系统 write),然后保存这些新的15个字节。在下一次迭代中,同样的情况再次发生。在缓冲区中的最后一次迭代之后是15 B,它们被写在文件的末尾(保留 with上下文)。

第二种情况,我将尝试将比缓冲区大小更多的数据写入缓冲区:

# Case 2
with open('test.log', 'wb', buffering=16) as f:
for _ in range(5):
f.write(b'a'*17)


strace输出:

write(3, "aaaaaaaaaaaaaaaaa", 17)       = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17
write(3, "aaaaaaaaaaaaaaaaa", 17) = 17


此处发生的情况是,在第一次迭代中,它将尝试写入缓冲区17 B,但无法放入缓冲区17 B,因此将其直接写入文件,并且缓冲区保持为空。这适用于每个迭代。

现在让我们看一下文本模式。

# Case 3
with open('test.log', 'w', buffering=16) as f:
for _ in range(5):
f.write('a'*8192)


strace输出:

write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 8192) = 8192


首先回想 pending_bytes的大小为8192B。在第一次迭代中,它将8192字节(来自代码: 'a'*8192)写入 pending_bytes缓冲区。在第二次迭代中,它向 pending_buffer添加了另一个8192字节,发现它大于8192( pending_bytes缓冲区的大小),并将其写入基础 _io.BufferedWriter()中。 _io.BufferedWriter()中的缓冲区的大小为16 B( buffering参数),因此它将立即写入文件(与情况2相同)。现在 pending_buffer为空,并且在第三次迭代中再次填充8192B。在第四次迭代中,它添加了另一个8192 B pending_bytes缓冲区溢出,并再次像第二次迭代一样直接写入文件。在最后一次迭代中,它向 pending_bytes缓冲区添加了8192 B,在关闭文件时将其刷新。

最后一个示例包含大于8192 B的缓冲。另外,为了更好地说明,我又添加了2个迭代。

# Case 4
with open('test.log', 'w', buffering=30000) as f:
for _ in range(7):
f.write('a'*8192)


strace输出:

write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 16384) = 16384
write(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 24576) = 24576


迭代次数:


将8192 B加到 pending_bytes中。
pending_bytes中添加8192 B,但是它大于最大大小,因此将其写入基础 _io.BufferedWritter()中并停留在该位置( pending_bytes现在为空)。
将8192 B加到 pending_bytes中。
pending_bytes中添加8192 B,但它大于最大大小,因此它尝试写入基础 _io.BufferedWritter()中。但这会超出底层缓冲区的最大容量,原因是 16384 + 16384 > 30000(从迭代2开始仍然存在第一个16384 B),因此它首先将旧的16384 B写入文件,然后将那些新的16384 B(来自 pending_bytes)放入缓冲区。 (现在 pending_bytes缓冲区再次为空)
与3相同
与4相同
当前 pending_buffer为空, _io.BufferedWritter()包含16384B。在此迭代中,它用8192 B填充 pending_buffer


当程序离开 with部分时,它将关闭文件。关闭过程如下:


pending_buffer中的8192 B写入 _io.BufferedWriter()(可能是 8192 + 16384 < 30000的原因)
将( 8192 + 16384 =)24576 B写入文件。
关闭文件描述符。


顺便说一句,目前我不知道为什么 pending_buffer可以用来缓冲 _io.BufferedWritter()的基础缓冲区。我的最佳猜测是因为它可以提高以文本模式工作的文件的性能。

关于python-3.x - Python3的开放缓冲参数看起来很奇怪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55360938/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com