【问题标题】:Most efficient way to check if elements in an array have changed检查数组中的元素是否已更改的最有效方法
【发布时间】:2014-02-07 13:33:16
【问题描述】:

我在 c 中有一个数组,只有当数组中的元素发生变化时,我才需要执行一些操作。然而,为此花费的时间和内存非常重要。我意识到这样做的一种有效方法可能是散列数组的所有元素并将结果与​​之前的结果进行比较。如果它们匹配,则意味着元素不会改变。但是,我想知道这是否是最有效的做事方式。另外,由于数组只有 8 个字节长(每个元素 1 个字节),哪个哈希函数最耗时?

数组中的元素实际上是从另一个微控制器接收的。因此,它们可能会或可能不会改变,具体取决于其他微控制器测量的内容是否相同

【问题讨论】:

  • 到目前为止你做了什么?
  • “将结果与之前的结果进行比较。...匹配意味着元素不改变”是不正确的。如果散列不同,则数组不同。如果哈希相同,它们可能相同。
  • 可以通过函数独占访问数组吗?如果是这样,那么它就像设置一个标志一样简单
  • 存储和比较 8 个字节。它不是计算密集型的
  • 如果数组只有 8 个字节长,那么简单地保留一份副本并进行比较几乎可以肯定是最快和最节省存储的。也就是说,如果您不能简单地保留一个在更改时设置的标志。

标签: c arrays performance hash


【解决方案1】:

如果您没有被绑定到一个简单的数组,您可以创建一个“MRU”结构列表,其中该结构可以包含一个标志,指示自上次检查后项目是否已更改。

每次项目更改时,设置“更改标志”并将其移动到列表的头部。当您需要检查更改的项目时,您从头部遍历列表并取消设置更改的标志并在未设置更改标志的第一个元素处停止。

抱歉,我错过了关于数组只有 8 个字节长的部分。有了这些信息和您编辑的新信息,我认为之前的建议并不理想。

如果数组只有 8 字节长,为什么不缓存前一个数组的副本并将其与收到的新数组进行比较?

以下是我对“快捷方式”比较的评论的澄清。如何实现这取决于所用平台上的 sizeof(int) 是什么。

使用 64 位整数,您可以通过比较来确定数组是否已更改。例如:

#define ARR_SIZE 8

unsigned char cachedArr[ARR_SIZE];
unsigned char targetArr[ARR_SIZE];

unsigned int *ic = (unsigned int *)cachedArr;
unsigned int *it = (unsigned int *)targetArr;

// This assertion needs to be true for this implementation to work 
// correctly.
assert(sizeof(int) == sizeof(cachedArr));

/* 
** ... 
** assume initialization and other suff here 
** leading into the main loop that is receiving the target array data.
** ...
*/

if (*ic != *it)
{
    // Target array has changed; find out which element(s) changed.
    // If you only cared that there was a change and did not care 
    // to know which specific element(s) had changed you could forego
    // this loop altogether.
    for (int i = 0; i < ARR_SIZE; i++)
    {
        if (cachedArr[i] != targetArr[i])
        {
            // Do whatever needs to be done based on the i'th element
            // changed
        }
    }

    // Cache the array again since it has changed.
    memcpy(cachedArr, targetArr, sizeof(cachedArr));
}
// else no change to the array

如果本机整数大小小于 64 位,您可以使用相同的理论,但您必须循环遍历数组 sizeof(cachedArr) / sizeof(unsigned int) 次;如果更改发生在最后一个测试块中,则会涉及最坏的情况(但并不总是存在)。

应该注意,在将任何 char 转换为整数类型时,您可能需要考虑对齐(如果 char 数据与适当的字长边界对齐)。

然而,进一步考虑这一点,最好自己展开循环并执行以下操作:

if (cachedArr[0] != targetArr[0])
{
    doElement0ChangedWork();
}
if (cachedArr[1] != targetArr[1])
{
    doElement1ChangedWork();
}
if (cachedArr[2] != targetArr[2])
{
    doElement2ChangedWork();
}
if (cachedArr[3] != targetArr[3])
{
    doElement3ChangedWork();
}
if (cachedArr[4] != targetArr[4])
{
    doElement4ChangedWork();
}
if (cachedArr[5] != targetArr[5])
{
    doElement5ChangedWork();
}
if (cachedArr[6] != targetArr[6])
{
    doElement6ChangedWork();
}
if (cachedArr[7] != targetArr[7])
{
    doElement7ChangedWork();
}

同样,取决于是否知道哪些特定元素发生了变化,可以收紧。这将导致需要更多的指令内存,但消除了循环开销(良好的旧内存与速度的权衡)。

与任何与时间/记忆相关的测试一样,测量、比较、调整和重复,直到达到预期的结果。

【讨论】:

  • 我不想去那里的唯一原因是我必须不断地比较字节,这在 MCU 的主循环中运行。所以我一直在寻找一种更有效的方法。
  • 您平台上的整数大小是多少?您可以尝试通过将指向数组的指针转换为无符号整数并以块进行比较来简化比较。只有当块不同时,您才必须逐字节比较该块。
  • 想一想:你可能合理使用的最小散列是 4 个字节。因此,要使用散列,您必须获取 8 个字节,散列成 4 个,获取旧的 4 字节散列,然后进行比较。因此,您使用散列获取至少 12 个,而比较获取 16 个,散列要复杂得多。如果哈希是性能赢家,那就太棒了。
  • 我不是在谈论散列。我试图在之前的编辑中澄清这一点。
【解决方案2】:

仅当数组中的元素发生变化时

除了你还有谁会改变他们?您只需跟踪自上次执行操作后是否进行了更改。

如果您不想这样做(可能是因为它需要在太多地方记录更改,或者因为记录保存会花费太多时间,或者因为另一个线程或其他硬件正在弄乱数组),只需将数组的旧内容保存在单独的数组中。它只有 8 个字节。当您想查看是否有任何更改时,将当前数组与复制的元素逐个元素进行比较。

【讨论】:

  • 并非如此。数组中的元素实际上是从另一个微控制器接收的。因此,根据其他微控制器测量的内容是否相同,它们是否会改变或可能不会改变
  • @user1667307:那么只需保留数组过去的样子。它只有 8 个字节。 (即使它大于 8 个字节,哈希也不会削减它,除非您可以接受误报。)
【解决方案3】:

正如其他人所说,元素只有在代码更改时才会更改。

也许其他用户可以更改此数据?否则你会知道你改变了一个条目。

就哈希函数而言,这个数组只能取 2^8 = 256 个不同的值。哈希函数在这里并没有真正的帮助。此外,必须计算哈希函数,这会消耗内存,所以我认为这不适用于您的应用程序。

我只会比较位,直到你发现一个已经改变。如果一个已经改变,你将在你的数组改变之前平均检查 4 位(假设每个位都同样可能改变)。

如果一个没有改变,那是最坏的情况,你必须检查所有八位才能得出没有改变的结论。

【讨论】:

    【解决方案4】:

    如果数组只有 8 个字节长,你可以把它当作一个 long long 类型的数字。假设原始数组是char data[8]。

    long long * pData = (logn long *)data;
    long long olddata = *pData;
    
    if ( olddata != *pData )
    {
          // detect which one changed
    }
    

    我的意思是,这样你一次操作所有数据,这比使用索引访问每个元素要快得多。在这种情况下,哈希比较慢。

    【讨论】:

      【解决方案5】:

      如果它是面向字节的,只有八个元素,执行 XOR 函数将比任何其他比较更有效。

      如果 ((LocalArray[0] ^ 收到数组 [0]) & (LocalArray[1] ^ 收到数组 [1]) & ...)

      {

      //是的,它改变了

      }

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-12-09
        • 2012-12-16
        • 1970-01-01
        • 1970-01-01
        • 2021-02-23
        • 1970-01-01
        相关资源
        最近更新 更多