【问题标题】:how to avoid the potential of an overflow when computing an average of times?计算平均次数时如何避免溢出的可能性?
【发布时间】:2017-08-18 21:09:00
【问题描述】:

我正在编写一个函数,用于获取调用特定 void (*)(void) aka void -> void 函数特定次数所需的时钟平均值。

我担心如果样本量太大,观察的总和会溢出,使平均值无效。

有没有一种标准方法可以消除这类问题中总和溢出的可能性?

注意:我知道这个例子太天真了,无法得出任何关于性能的结论;我有兴趣消除总和溢出的可能性,而不是在性能方面做出任何结论。

注 2:我也知道除非程序运行数百年,否则 64 位无符号数实际上不会溢出,但我很好奇是否也可以消除这个假设。

这是我的独立代码:

#include <Windows.h>
#include <stdio.h>

/**
 * i want to parametrize the type which is used to store sample size
 * to see whether it impacts performance 
 */
template <typename sampleunit_t>
static inline ULONGLONG AveragePerformanceClocks (void (*f)(),  sampleunit_t nSamples)
{
    ULONGLONG sum;
    sampleunit_t i;    

    sum = 0;

    for (i = 0; i < nSamples; ++i) {
        LARGE_INTEGER t1; 
        LARGE_INTEGER t2;
        ULONGLONG dt;

        QueryPerformanceCounter(&t1);
        f();        
        QueryPerformanceCounter(&t2);

        dt = t2.QuadPart - t1.QuadPart;

        // sum may possibly overflow if program runs long enough with
        // a large enough nSamples
        sum += dt;
    }


    return (ULONGLONG)(sum / nSamples);
}

/* a cdecl callback that consumes time */
static void test1() 
{
    // don't optimize
    volatile int i;

    for (i = 0; i < 10000; ++i) {

    }
}

int main(int argc, char **argv)
{
    ULONGLONG avg;

    avg = AveragePerformanceClocks<BYTE>(test1, 255);    
    printf("average clocks(truncated): %llu.\n", avg);   

    avg = AveragePerformanceClocks<WORD>(test1, 255);    
    printf("average clocks(truncated): %llu.\n", avg);   

    avg = AveragePerformanceClocks<DWORD>(test1, 255);    
    printf("average clocks(truncated): %llu.\n", avg);   

    avg = AveragePerformanceClocks<ULONGLONG>(test1, 255);    
    printf("average clocks(truncated): %llu.\n", avg);   

    system("pause");

    return 0;
}

【问题讨论】:

标签: c++ winapi average


【解决方案1】:

前n个元素的平均值是

          SUM
Average = ---
           n

下一个元素 Mi 是

           (SUM + Mi)
Average2 = ----------
              n + 1

因此,鉴于当前平均值,可以找到具有新读数的下一个平均值。

           (Average * n + Mi )
Average2 = -------------------
                  n + 1

然后可以将其更改为不增加的等式

                       n      Mi
Average2 = Average * ----- + -----
                     n + 1   n + 1

在实践中,时间的大小将适合计算机的数据类型。

正如所指出的,这需要使用浮点表示,虽然不会因溢出而失败,但当n/(n+1) 小于浮点小数部分的精度时仍然会失败。

更新

来自incremental average

有更好的重组。

                       Mi - Average
Average2 = Average  +  -------------
                           n + 1

更好,因为它只有一个部门。

【讨论】:

  • 如果我错了,请纠正我,但是除非使用浮点执行平均,否则这不会导致额外的截断吗?我开始认为如果不切换到有符号时间来纠正溢出,即使是两个样本也可能无法解决;并且浮点和整数方法都可以得到累积截断错误。
  • 我认为这可能是唯一的解决方案;在内部将平均值计算为双精度值。
【解决方案2】:

您可以通过将值 dt/nSamples 添加到 sum 来减少溢出的可能性,同时确保不会丢失 dt%nSamples

template <typename sampleunit_t>
static inline ULONGLONG AveragePerformanceClocks (void (*f)(),
                                                  sampleunit_t nSamples)

{
    ULONGLONG delta = 0;
    ULONGLONG sum = 0;
    sampleunit_t i;    

    for (i = 0; i < nSamples; ++i) {
        LARGE_INTEGER t1; 
        LARGE_INTEGER t2;
        ULONGLONG dt;

        QueryPerformanceCounter(&t1);
        f();        
        QueryPerformanceCounter(&t2);

        dt = t2.QuadPart - t1.QuadPart;

        // Reduce the potential for overflow.
        delta += (dt%nSamples);
        sum += (dt/nSamples);
        sum += (delta/nSamples);
        delta = (delta%nSamples);
    }

    return sum;
}

【讨论】:

    【解决方案3】:

    为了防止计算中的和值溢出,您可以标准化基值:

    假设您的输入是数据:

    20
    20
    20
    20
    20
    

    sum 为 100,average 为 20,count 为 5。

    如果现在要添加一个新值 30,并且我将使用一个 7 位整数作为值来存储 sum,您会遇到溢出问题。

    诀窍是标准化:

    1. 取新值 30,除以 average 值,我们称之为 new_val_norm
    2. average 的值除以average(即1.000),然后乘以count,我们称之为avg_norm
    3. 将新值new_val_norm 添加到avg_norm 值,除以count+1(我们刚刚添加了一个额外值),然后乘以average 得到新的平均值。

    然后将溢出的风险推到总和中,因为它不再使用了。

    如果 avg * count (avg_norm) 仍然很大,您还可以选择将新值除以 avg 和 count,然后再加 1。

    【讨论】:

      猜你喜欢
      • 2020-12-06
      • 2021-09-04
      • 2012-05-23
      • 1970-01-01
      • 2021-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多