首页 > 解决方案 > 如何在 C++ 或 Python 中快速将二进制文件划分为 6 字节块?

问题描述

我正在将 C++ 和 Python 中的文件作为二进制文件读取。我需要将二进制文件分成块,每块 6 个字节。例如,如果我的文件是 600 字节,那么结果应该是 100 个块,每个块 6 个字节。

我已经尝试过结构(在 C++ 和 Python 中)和数组(Python)。它们都没有将二进制文件分成 6 个字节的块。他们只能将二进制文件分成每个 2 的幂(1、2、4、8、16 等)的块。

数组算法非常快,可以在不到一秒的时间内以 4 字节块的形式读取 1 GB 的二进制数据。相比之下,我用了一些其他的方法,但是都非常慢,几十分钟就搞定了几兆。

如何尽可能快地将二进制文件读取为 6 个字节的块?C++ 或 Python 中的任何帮助都会很棒。谢谢你。

编辑 - 代码:

    struct Block
{
    char data[6];
};

class BinaryData
{
private:
    char data[6];

public:
    BinaryData() {};
    ~BinaryData() {};

    void readBinaryFile(string strFile)
    {
        Block block;
        ifstream binaryFile;
        int size = 0;

        binaryFile.open(strFile, ios::out | ios::binary);
        binaryFile.seekg(0, ios::end);
        size = (int)binaryFile.tellg();
        binaryFile.seekg(0, ios::beg);
        cout << size << endl;
        while ( (int)binaryFile.tellg() < size )
        {
            cout << binaryFile.tellg() << " , " << size << " , " << 
size - (int)binaryFile.tellg() << endl;
            binaryFile.read((char*)block.data,sizeof(block.data));
            cout << block.data << endl;
            //cin  >> block.data;
            if (size - (int)binaryFile.tellg() > size)
            {
                break;
            }
        }
        binaryFile.close();

    }

};

备注:

标签: pythonc++binary

解决方案


让我们从简单开始,然后进行优化。

简单循环

uint8_t  array1[6];
while (my_file.read((char *) &array1[0], 6))
{
    Process_Block(&array1[0]);
}

上面的代码读取一个文件,一次 6 个字节,并将块发送到一个函数。
符合要求,不是很理想。

读取更大的块

文件是流媒体设备。他们有开始流式传输的开销,但保持流式传输非常有效。换句话说,我们希望每个事务读取尽可能多的数据以减少开销。

static const unsigned int CAPACITY = 6 * 1024;
uint8_t block1[CAPACITY];
while (my_file.read((char *) &block1[0], CAPACITY))
{
    const size_t bytes_read = my_file.gcount();
    const size_t blocks_read = bytes_read / 6;
    uint8_t const * block_pointer = &block1[0];
    while (blocks_read > 0)
    {
        Process_Block(block_pointer);
        block_pointer += 6;
        --blocks_read;
    }
}

上面的代码在一个事务中最多读取 1024 个块。读取后,将每个块发送到一个函数进行处理。

这个版本比简单循环更有效,因为它在每个事务中读取更多数据。 调整 CAPACITY 以在您的平台上找到最佳尺寸。

循环展开

前面的代码减少了输入传输速度的第一个瓶颈(尽管仍有优化的空间)。另一种技术是通过在循环内执行更多数据处理来减少处理循环的开销。这称为循环展开

const size_t bytes_read = my_file.gcount();
const size_t blocks_read = bytes_read / 6;
uint8_t const * block_pointer = &block1[0];
while ((blocks_read / 4) != 0)
{
    Process_Block(block_pointer);
    block_pointer += 6;

    Process_Block(block_pointer);
    block_pointer += 6;

    Process_Block(block_pointer);
    block_pointer += 6;

    Process_Block(block_pointer);
    block_pointer += 6;
    blocks_read -= 4;
}
while (blocks_read > 0)
{
    Process_Block(block_pointer);
    block_pointer += 6;
    --blocks_read;
}

您可以调整循环中的操作数量,以查看它如何影响程序的速度。

多线程和多个缓冲区

另外两种加快数据读取速度的技术是使用多线程和多缓冲区。

一个线程,一个输入线程,将文件读入缓冲区。读入第一个缓冲区后,线程设置一个信号量,指示有数据要处理。输入线程读入下一个缓冲区。如此重复直到数据全部被读取。(对于一个挑战,弄清楚如何重用缓冲区并通知其他线程哪些缓冲区可用)。

第二个线程是处理线程。该处理线程首先启动并等待第一个缓冲区被完全读取。缓冲区有数据后,处理线程开始处理数据。处理完第一个缓冲区后,处理线程从下一个缓冲区开始。重复此过程,直到处理完所有缓冲区。

这里的目标是使用尽可能多的缓冲区来保持处理线程运行而不是等待。

编辑 1:其他技术

内存映射文件

一些操作系统支持内存映射文件。操作系统将文件的一部分读入内存。当访问内存之外的位置时,操作系统会将另一部分加载到内存中。需要测量(分析)这种技术是否提高了性能。

并行处理和线程

添加多个线程可能会显示微不足道的性能增益。计算机有一条数据总线(数据高速公路)连接许多硬件设备,包括内存、文件 I/O 和处理器。设备将被暂停以让其他设备使用数据高速公路。对于多个内核或处理器,一个处理器可能需要等待,而另一个处理器正在使用数据高速公路。当使用多线程或并行处理时,这种等待可能会导致可忽略的性能增益。此外,操作系统在构建和维护线程时也会产生开销。


推荐阅读