C#-对大文件进行霍夫曼编码需要太长时间

时间:2018-11-14 21:03:46

标签: c# compression huffman-code

我正在尝试在C#中实现霍夫曼编码。我对大型文件进行编码存在问题,因为这需要花费太多时间。例如,要编码11MiB二进制文件,在调试模式下需要10秒。而且,我什至不必费心等待程序完成27MiB文件。

这是有问题的循环:

            BitArray bits = new BitArray(8);
            byte[] byteToWrite = new byte[1];
            byte bitsSet = 0;

            while ((bytesRead = inputStream.Read(buffer, 0, 4096)) > 0) // Read input in chunks
            {
                for (int i = 0; i < bytesRead; i++)
                {
                    for (int j = 0; j < nodesBitStream[buffer[i]].Count; j++)
                    {
                        if (bitsSet != 8)
                        {
                            bits[bitsSet] = nodesBitStream[buffer[i]][j];
                            bitsSet++;
                        }
                        else
                        {
                            bits.CopyTo(byteToWrite, 0);
                            outputStream.Write(byteToWrite, 0, byteToWrite.Length);
                            bits = new BitArray(8);
                            bitsSet = 0;

                            bits[bitsSet] = nodesBitStream[buffer[i]][j];
                            bitsSet++;
                        }
                    }
                }
            }

nodesBitStreamDictionary<byte, List<bool>>List<bool>表示从霍夫曼树根到叶节点的路径,该路径包含以byte表示的特定符号。

因此,我正在累积位以形成一个字节,然后将其写入已编码的文件。很明显,这可能需要很长时间,但是我还没有找到其他方法。因此,我正在寻求有关如何加快流程的建议。

2 个答案:

答案 0 :(得分:2)

一点一点地工作是很多额外的工作。同样,尽管Dictionary<byte, TVal>不错,但普通数组甚至更快。

霍夫曼码也可以表示为一对整数,一个整数表示长度(以位为单位),另一个表示位数。在这种表示形式中,您可以通过几个快速操作来处理符号,例如(未测试):

BinaryWriter w = new BinaryWriter(outStream);
uint buffer = 0;
int bufbits = 0;
for (int i = 0; i < symbols.Length; i++)
{
    int s = symbols[i];
    buffer <<= lengths[s];  // make room for the bits
    bufbits += lengths[s];  // buffer got longer
    buffer |= values[s];    // put in the bits corresponding to the symbol

    while (bufbits >= 8)    // as long as there is at least a byte in the buffer
    {
        bufbits -= 8;       // forget it's there
        w.Write((byte)(buffer >> bufbits)); // and save it
    }
}
if (bufbits != 0)
    w.Write((byte)(buffer << (8 - bufbits)));

或某些变体,例如,您可以以其他方式填充字节,或将字节保存在数组中并进行更大的写操作,等等。

此代码要求将代码长度限制为最大25位,通常其他要求会进一步降低该限制。不需要很大的代码长度即可获得良好的压缩率。

答案 1 :(得分:2)

我真的不知道算法的工作原理,但是看一下您的代码,有两点值得关注:

  1. 您似乎正在使用字典来索引一个字节。使用List<bool>[]索引到一个简单的buffer[i]可能会更快。您要支付的内存价格相当低。使用数组可以交换具有更快偏移量的查找。您正在那里进行很多查找。

  2. 为什么要在每次迭代中实例化bits?取决于您执行的迭代次数,最终可能会给GC带来压力。似乎没有必要,您实际上是在覆盖每个位,然后每8位将其吐出,因此只需覆盖它,而不是对其进行更新;一遍又一遍地使用同一实例。