使用Python计算SHA256时POSIX_FADV_DONTNEED的效果实测:不建议使用
POSIX_FADV_DONTNEED 实测性能记录
更新:经过更多测试后,本文主要用于记录实测结果。
TL;DR:不建议使用POSIX_FADV_DONTNEED,不使用该标志时速度最快,在AMD64架构下该标志甚至未被内核遵守。
测试环境
- 树莓派4,USB3.0连接5TB机械硬盘,ext4文件系统
- 该配置下使用1线程速度最优,推测原因是机械硬盘特性
实测结果
本次测试场景为计算目录树中所有文件的SHA256哈希值(无需输入加密密码即可检查多个restic仓库):
- 监控工具(nmon、node_exporter)显示使用
POSIX_FADV_DONTNEED时磁盘读取速度几乎翻倍。该标志的作用是告知内核无需将读取的数据保留在缓存中,理论上因文件仅读取一次,可避免污染系统缓存导致其他数据缓存失效。 - 实际测速与监控数据存在差异:不使用
POSIX_FADV_DONTNEED时,读取速度为60-90MB/s;使用时为155-175MB/s,但time命令统计的实际耗时完全不同。
每次测试前均执行缓存清理命令:
sync; echo 3 > /proc/sys/vm/drop_caches
树莓派4测试数据
- 使用
posix_fadvise(fd, 0, bytesRead):耗时37秒,监控显示磁盘速度慢 - 使用
posix_fadvise(fd, 0, 0):监控显示磁盘速度翻倍,但实际耗时1分08秒 - 使用空实现的
posix_fadvise函数:仅耗时29秒,是最快的结果
注:树莓派上磁盘速度显示不准确,AMD64平台的监控数据更准确;但树莓派上VictoriaMetrics显示使用
POSIX_FADV_DONTNEED时缓存未增长,说明该标志被内核遵守。
其他场景补充测试
- SSD高性能云虚拟机:使用4线程时,
POSIX_FADV_DONTNEED会导致速度慢5倍(测试前同样执行echo 3 > /proc/sys/vm/drop_caches清理缓存) - USB3.0连接机械硬盘的物理机:单线程使用
POSIX_FADV_DONTNEED耗时1分25秒;清理缓存后不使用该标志仅耗时12秒,速度差7倍
2023.09.20 AMD64架构更新
VictoriaMetrics显示AMD64架构下POSIX_FADV_DONTNEED未被内核遵守,缓存仍在增长:
- 使用
posix_fadvise(fd, 0, bytesRead)与空实现函数的time结果无明显差异 - 使用
posix_fadvise(fd, 0, 0)耗时2分31秒
测试代码
import os import subprocess import hashlib import concurrent.futures import sys import ctypes # Constants for posix_fadvise POSIX_FADV_DONTNEED = 4 base_directory = '/home/pi/5TB' num_threads = 1 # Adjust the number of threads as needed # Define posix_fadvise function def posix_fadvise(fd, offset, length): #return #uncomment and speed will be much slower libc = ctypes.CDLL("libc.so.6") ret = libc.posix_fadvise(fd, offset, length, POSIX_FADV_DONTNEED) if ret != 0: raise OSError(f"posix_fadvise failed with error code {ret}") def calculate_sha256(file_path): try: # Calculate the SHA256 checksum of the file sha256_hash = hashlib.sha256() bytesRead = 0 # Initialize the counter for bytes read with open(file_path, 'rb') as f: fd = f.fileno() # Get file descriptor # Advise the kernel that we don't need the file data anymore #posix_fadvise(fd, 0, 0) while True: data = f.read(65536) # Read in 64KB chunks if not data: break bytesRead += len(data) sha256_hash.update(data) posix_fadvise(fd, 0, bytesRead) checksum = sha256_hash.hexdigest() # Check if the checksum matches the filename filename = os.path.basename(file_path) if checksum != filename: sys.stderr.write(f"Error: Checksum mismatch for file '{file_path}'\n") return file_path except Exception as e: sys.stderr.write(f"Error processing file '{file_path}': {str(e)}\n") return None def process_files_in_directory(directory): files = [os.path.join(directory, filename) for filename in os.listdir(directory) if os.path.isfile(os.path.join(directory, filename))] results = [] with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor: for file in executor.map(calculate_sha256, files): if file is not None: results.append(file) return results if __name__ == "__main__": checked_count = 0 for root, _, _ in os.walk(base_directory): checked_files = process_files_in_directory(root) checked_count += len(checked_files) if checked_count % 100 == 0: sys.stdout.write(f"Checked {checked_count} files...\n") sys.stdout.flush() # Flush the stdout buffer to write immediately sys.stdout.write(f"Checked {checked_count} files in total.\n")
内容的提问来源于stack exchange,提问作者Hannes
相关产品推荐
相关产品推荐

