C库用于压缩连续的正整数(C Library for compressing sequential

2019-07-29 15:56发布

我有一个在磁盘阵列的字符串创建索引的非常普遍的问题。 总之,我需要存储在中盘表示每个字符串的位置。 例如,如下一个非常幼稚的解决办法是索引阵列:

UINT64 IDX [] = {0,20,500,1024,...,103434};

它说的是,第一字符串是在位置0,第二个20位,第三,在500位置和103434位置的第n个。

位置总是按顺序非负64位整数。 虽然数字可以由任何差异而变化,但在实践我期望典型差为该范围的内部从2 ^ 8至2 ^ 20。 我期望这个索引来在存储器中mmap'ed,和位置将被随机访问(假定均匀分布)。

我在想我自己写的代码做某种块增量编码或其它更复杂的编码,但也有编码/解码速度和空间之间这么多不同的取舍,我宁愿得到一个工作库为起点甚至可能解决的东西,没有任何自定义。

任何提示? C库将是理想的,但C ++的一个也可以让我运行一些初始的基准。

如果你还在下面的详细原因。 这将被用来建立类似于CDB库( http://cr.yp.to/cdb/cdbmake.html顶部的库CMPH() http://cmph.sf.net )。 总之,它是基于一个大的磁盘读取只在内存小的指数关联地图。

既然是一个图书馆,我没有在输入控件,但我要优化典型的用例有数亿值的,在几KB平均值尺寸范围在2 ^ 31最大值。

为了记录在案,如果我没有找到现成使用图书馆,我打算实施的64个整数与指定块到目前为止抵消初始字节的块增量编码。 该块本身将与树进行索引,让我O(日志(N / 64))的访问时间。 有太多的其他选择,我宁愿不讨论这些问题。 我真的很期待准备使用的代码,而不是如何实现编码的想法。 我会很高兴和大家一起分享我做什么,一旦我有工作。

我感谢您的帮助,让我知道,如果您有任何疑问。

Answer 1:

我用fastbit (咳声吴LBL.GOV),看来你需要的东西好,速度快而现在,所以fastbit是Oracle的BBC(字节对齐位码,BerkeleyDB的)高度competient改善。 这很容易安装和非常好的gernally。

然而,考虑到更多的时间,你可能想看看在格雷码的解决方案,它似乎是最适合你的目的。

丹尼尔·勒迈尔有一些关于发布了C / ++ / Java库code.google ,我读了他的一些论文和他们在fastbit相当不错,几个进步和替代方法列重新排序与置换的灰色代码的。

差点忘了,我也碰到东京内阁 ,但我不认为这将是非常适合我目前的项目,我可能更多的考虑,如果我以前知道这件事),它有一个很大程度的互操作性,

东京内阁是用C语言,如C,Perl中,红宝石,爪哇,和Lua的API提供。 东京柜可以用具有API符合C99和POSIX平台。

至于你提到CDB的TC基准具有TC模式(TC支持的几种操作约束对不同PERF),它超越了国家开发银行10倍的读取性能和2倍的写入。

对于您的增量编码的要求,我在相当有信心bsdiff和它的到外进行任何的file.exe内容修补系统的能力,它也可能对您的日常需求的一些fundimental接口。

谷歌的新的二进制压缩应用程序, 胡瓜可能是值得一试,如果你错过了新闻稿,10级小差异的比在一个测试情况下,我已经看到了公布bsdiff。



Answer 2:

你有两个相互矛盾的需求:

  1. 要压缩非常小件物品(每个8个字节)。
  2. 你需要为每个项目高效的随机访问。

第二个要求是非常有可能施加一个固定长度的每个项目。



Answer 3:

究竟什么是你想压缩? 如果你正在考虑指数的总空间,是不是真的值得节省空间的努力?

如果是这样一两件事你可以尝试是砍空间分成一半,将其存储到两个表。 第一存储(上部UINT,开始索引,长度,指针到第二表)和第二将存储(索引,低级UINT)。

为了快速搜索,索引将使用类似实施B +树 。



Answer 4:

我做了一件类似几年前的全文搜索引擎。 在我的情况下,每个索引词产生其中包括创纪录的数字(文档ID)和字号码(它可以很容易地存储了字偏移),它需要被压缩,尽可能的记录。 我用它注意到这一事实,将有一批文档中的同一个词的出现的优势增量压缩技术,这样的记录数往往并不需要在所有的重复。 和字偏移量增量往往会适合一个或两个字节内。 下面是我使用的代码。

因为它是在C ++中,代码可能不会对你有用原样,但可以写压缩例程一个很好的起点。

请原谅匈牙利命名法和幻数的代码中散落。 就像我说的,我这个在很多年前写的:-)

IndexCompressor.h

//
// index compressor class
//

#pragma once

#include "File.h"

const int IC_BUFFER_SIZE = 8192;

//
// index compressor
//
class IndexCompressor
{
private :
   File        *m_pFile;
   WA_DWORD    m_dwRecNo;
   WA_DWORD    m_dwWordNo;
   WA_DWORD    m_dwRecordCount;
   WA_DWORD    m_dwHitCount;

   WA_BYTE     m_byBuffer[IC_BUFFER_SIZE];
   WA_DWORD    m_dwBytes;

   bool        m_bDebugDump;

   void FlushBuffer(void);

public :
   IndexCompressor(void) { m_pFile = 0; m_bDebugDump = false; }
   ~IndexCompressor(void) {}

   void Attach(File& File) { m_pFile = &File; }

   void Begin(void);
   void Add(WA_DWORD dwRecNo, WA_DWORD dwWordNo);
   void End(void);

   WA_DWORD GetRecordCount(void) { return m_dwRecordCount; }
   WA_DWORD GetHitCount(void) { return m_dwHitCount; }

   void DebugDump(void) { m_bDebugDump = true; }
};

IndexCompressor.cpp

//
// index compressor class
//

#include "stdafx.h"
#include "IndexCompressor.h"

void IndexCompressor::FlushBuffer(void)
{
   ASSERT(m_pFile != 0);

   if (m_dwBytes > 0)
   {
      m_pFile->Write(m_byBuffer, m_dwBytes);
      m_dwBytes = 0;
   }
}

void IndexCompressor::Begin(void)
{
   ASSERT(m_pFile != 0);
   m_dwRecNo = m_dwWordNo = m_dwRecordCount = m_dwHitCount = 0;
   m_dwBytes = 0;
}

void IndexCompressor::Add(WA_DWORD dwRecNo, WA_DWORD dwWordNo)
{
   ASSERT(m_pFile != 0);
   WA_BYTE buffer[16];
   int nbytes = 1;

   ASSERT(dwRecNo >= m_dwRecNo);

   if (dwRecNo != m_dwRecNo)
      m_dwWordNo = 0;
   if (m_dwRecordCount == 0 || dwRecNo != m_dwRecNo)
      ++m_dwRecordCount;
   ++m_dwHitCount;

   WA_DWORD dwRecNoDelta = dwRecNo - m_dwRecNo;
   WA_DWORD dwWordNoDelta = dwWordNo - m_dwWordNo;

   if (m_bDebugDump)
   {
      TRACE("%8X[%8X] %8X[%8X] : ", dwRecNo, dwRecNoDelta, dwWordNo, dwWordNoDelta);
   }

   // 1WWWWWWW
   if (dwRecNoDelta == 0 && dwWordNoDelta < 128)
   {
      buffer[0] = 0x80 | WA_BYTE(dwWordNoDelta);
   }
   // 01WWWWWW WWWWWWWW
   else if (dwRecNoDelta == 0 && dwWordNoDelta < 16384)
   {
      buffer[0] = 0x40 | WA_BYTE(dwWordNoDelta >> 8);
      buffer[1] = WA_BYTE(dwWordNoDelta & 0x00ff);
      nbytes += sizeof(WA_BYTE);
   }
   // 001RRRRR WWWWWWWW WWWWWWWW
   else if (dwRecNoDelta < 32 && dwWordNoDelta < 65536)
   {
      buffer[0] = 0x20 | WA_BYTE(dwRecNoDelta);
      WA_WORD *p = (WA_WORD *) (buffer+1);
      *p = WA_WORD(dwWordNoDelta);
      nbytes += sizeof(WA_WORD);
   }
   else
   {
      // 0001rrww
      buffer[0] = 0x10;

      // encode recno
      if (dwRecNoDelta < 256)
      {
         buffer[nbytes] = WA_BYTE(dwRecNoDelta);
         nbytes += sizeof(WA_BYTE);
      }
      else if (dwRecNoDelta < 65536)
      {
         buffer[0] |= 0x04;
         WA_WORD *p = (WA_WORD *) (buffer+nbytes);
         *p = WA_WORD(dwRecNoDelta);
         nbytes += sizeof(WA_WORD);
      }
      else
      {
         buffer[0] |= 0x08;
         WA_DWORD *p = (WA_DWORD *) (buffer+nbytes);
         *p = dwRecNoDelta;
         nbytes += sizeof(WA_DWORD);
      }

      // encode wordno
      if (dwWordNoDelta < 256)
      {
         buffer[nbytes] = WA_BYTE(dwWordNoDelta);
         nbytes += sizeof(WA_BYTE);
      }
      else if (dwWordNoDelta < 65536)
      {
         buffer[0] |= 0x01;
         WA_WORD *p = (WA_WORD *) (buffer+nbytes);
         *p = WA_WORD(dwWordNoDelta);
         nbytes += sizeof(WA_WORD);
      }
      else
      {
         buffer[0] |= 0x02;
         WA_DWORD *p = (WA_DWORD *) (buffer+nbytes);
         *p = dwWordNoDelta;
         nbytes += sizeof(WA_DWORD);
      }
   }

   // update current setting
   m_dwRecNo = dwRecNo;
   m_dwWordNo = dwWordNo;

   // add compressed data to buffer
   ASSERT(buffer[0] != 0);
   ASSERT(nbytes > 0 && nbytes < 10);
   if (m_dwBytes + nbytes > IC_BUFFER_SIZE)
      FlushBuffer();
   CopyMemory(m_byBuffer + m_dwBytes, buffer, nbytes);
   m_dwBytes += nbytes;

   if (m_bDebugDump)
   {
      for (int i = 0; i < nbytes; ++i)
         TRACE("%02X ", buffer[i]);
      TRACE("\n");
   }
}

void IndexCompressor::End(void)
{
   FlushBuffer();
   m_pFile->Write(WA_BYTE(0));
}


Answer 5:

你已经忽略你打算指数字符串的一些关键信息。

但考虑到你说你希望索引的字符串的最小长度为256,存储指数为64%, 至多 3%的开销招致。 如果字符串文件的总长度小于4GB,你可以使用32位的索引和收取1.5%的开销。 这些数字表明对我说,如果压缩的问题, 你就要去压缩字符串,而不是指数更好 。 对于这个问题上LZ77的变化似乎是为了。

如果你想尝试一个大胆的想法,把每串在一个单独的文件,它们全部拉成一个zip文件,看你怎么可以做zziplib 。 这可能不会很大,但它是在您的部分几乎为零的工作。

对这个问题的更多数据将受到欢迎:

  • 弦数
  • 字符串的平均长度
  • 字符串的最大长度
  • 字符串的长度中位数
  • 程度的字符串文件压缩与gzip
  • 无论您是允许更改的字符串,以提高压缩的顺序

编辑

注释和修订问题使问题更加清晰。 我喜欢你的分组的想法,我会尝试一个简单的增量编码,组三角洲和各组内使用可变长度编码。 我会在64线不作为组大小,我想你可能会想,以确定经验。

你问对现有库。 对于分组和增量编码我怀疑你会发现很多。 对于可变长度整数代码,我没有看到太多的C库的方式,但你可以找到在可变长度值编码的Perl和Python的 。 有一吨的文件和关于这一主题的一些专利,我怀疑你会风有推出自己的。 但也有一些简单的代码在那里,你可以给UTF-8一试,它可以编码无符号整数高达32位,你可以从抢C代码计划9 ,我敢肯定,许多其他来源。



Answer 6:

你是在Windows上运行? 如果是的话,我建议使用幼稚的解决方案的最初提议创建MMAP文件,然后压缩使用NTLM压缩文件。 您的应用程序代码从来不知道文件被压缩,而OS的确文件压缩为您服务。 你可能不认为这将是非常高性能或获得良好的压缩,但我认为,如果你尝试了,你会感到惊讶。



文章来源: C Library for compressing sequential positive integers