以正确的顺序读取内存需要一些帮助

时间:2011-07-13 20:36:47

标签: c algorithm

我们将某种记录存储在内存位置,如下所示

----------------------------------------------
|EventID | Timestamp | Variable Data | Length  |
----------------------------------------------

这些字段的长度如下

EventID +时间戳是12个字节 长度字段为4个字节,表示数据字段的长度。

数以百万计的这类记录一个接一个地放在一起。我有一个指向当前索引的指针,所以如果我想读取所有记录,我就这样读了4个字节从右到左&我取了那个特别的记录&迭代地这样做我读了完整的内存空间。但是这种方法的问题在于,与输入的顺序相比,它以相反的顺序读取记录。

我需要设置一个方法,这个方法允许我以最小的空间复杂度输入相同的顺序读取这些内存记录。

5 个答案:

答案 0 :(得分:4)

我有另一个很棒的解决方案!

  1. 以相反的顺序(从头到尾)读取您的记录,并交换EventIDLength字段的内存中值。
  2. 访问行时,请记住新布局:Length | Timestamp | Data | EventID

答案 1 :(得分:3)

由于可变长度数据部分位于长度之前,因此无法从开始存储器地址开始读取数据。假设不能对体系结构或存储进行任何更改,一种可能的选择是使用当前系统来构建可变数据长度的索引。然后,一旦到达数据的开头,您就会以正确的顺序读取记录 - 使用先前构建的索引来确定可变数据长度。

但是,您提到此数据集包含数百万条记录。因此,在处理之前存储所有可变数据长度的索引可能是不可行的。这个问题的一个解决方案是仅对每个其他条目或每四分之一,八等进行索引......具体取决于您的具体要求。然后你可以从每个索引记录开始,向后工作暂时保存数据长度,直到你达到你没有处理的记录。然后使用此保存的数据再次前进。

例如,假设您为第一次传递的每8条记录编制索引。然后,您将从记录8开始并保存该记录的长度。然后转到7,6,5,4,3,2,1。现在你已经保存了接下来的8个长度。所以过程记录1,2,3,4,5,6,7和8.现在,你不知道9的长度 - 所以跳到16.然后记录16,15,14,...,9长度。然后再像前面的过程9,10,11 ......那样重复。

答案 2 :(得分:0)

首先在提取时尝试“反转”记录顺序,然后使用相同的进程进行第二次提取(分配相同的内存量进行反转)。

由于变量数据具有可变长度,并且长度值在最后位置,因此我看不到从左到右获取此数据。

答案 3 :(得分:0)

还有另一种方法可以找到没有额外内存的行尾。

  1. 所有EventID都属于明确的范围,可以是顺序的
  2. 所有Timestamp也有明确的范围(例如,从2009/09/09到2011/11/11)
  3. LengthEventIDTimestamp在两行之间相邻,总长度固定为16个字节(长度为4个,eventID为4个,时间戳为8个)。
  4. 在这些考虑因素下,您可以编写一个搜索行尾的函数,例如

    byte* FindNextRow(byte* rowStart, byte* memBlockEnd,
                     DWORD minEventID, DWORD maxEventID,
                     QWORD minTimestamp, QWORD maxTimestamp)
    {
      long bytesAvail = (long)(memBlockEnd - rowStart) - 4;
      byte* ptr = rowStart + 12; // move to 'data'
    
      for (long i = 0; i < bytesAvail; i++, ptr++) {
    
        long length = *(long*)(ptr);
    
        // check if this is the last row
        if (ptr + 4 == memBlockEnd)
          return memBlockEnd;
    
        // try to find candidate for 'length' field first
        if (rowStart + 12 != ptr - length)
          continue;
    
        // then check 'EventID' and 'Timestamp' for the next row
        DWORD eventID = *(DWORD*)(ptr + 4);
        if (eventID < minEventID || eventID > maxEventID)
          continue; // you might add additional check on a sequence: eventID + 1 == *(DWORD*)(rowStart);
    
        QWORD timestamp = *(QWORD*)(ptr + 8);
        if (timestamp < minTimestamp || timestamp > maxTimestamp)
          continue; // you might add additional check on a sequence: timestamp > *(QWORD*)(rowStart + 4);
    
        // this is the match
        return ptr + 4;
      }
    }
    

    警告:这不能保证正确性,但您可以尝试以这种方式找到解决方法。

答案 4 :(得分:0)

是否为您接受的每条消息分配一个指针(32位机器,通常为4个字节)?

如果是,你可以,从最后开始:

  1. 读取当前位置的长度 - 4
  2. 获取指向事件ID的第一个字节的指针:当前位置 - 4 - 长度 - 12
  3. 根据需要调整指针数组的大小
  4. 将指针存储在数组中
  5. 从1
  6. 重复

    当然,随着指针数组的增长,你需要realloc()(不需要每次重新分配,以块为单位)。

    我假设您将它们视为char数组,因此连续元素(n和n-1)的char指针差异将为您提供整个消息的大小。

    这浪费了记忆力。我知道你不想,但如果你不能像Opillect那样说,交换EventID和Length字段,因为它们有不同的大小,这似乎是一个很好的方法。