【问题标题】:Efficient way to skip code every X iterations?每 X 次迭代跳过代码的有效方法?
【发布时间】:2015-10-02 19:32:46
【问题描述】:

我正在使用 GameMaker Studio,你可以把它想象成一个巨大的循环。

我使用计数器变量step 来跟踪它是什么帧。

为了提高效率,我只想每 X 步运行一些代码。

if step mod 60 {

}

将每 60 步(或 1 秒以 60 fps)运行该块。

我的理解是,模数是一项繁重的操作,我认为计算可能会因数千步而失控。有没有更有效的方法来做到这一点?

可能涉及位运算符?

我知道这适用于所有其他帧:

// Declare
counter = 0

// Step
counter = (counter + 1) & 1

if counter {

}

或者在 60FPS 时模数对性能的影响可以忽略不计,即使数字很大?

【问题讨论】:

  • 一般来说,我的态度是在你不得不担心之前不要担心性能。尤其是在这么低的水平上。与加法相比,模数很重,但与字符串比较或其他东西相比可能很轻。如果我的想法是正确的,那么模数就是 3 次算术运算,所以实际上是零时间。
  • 你为什么不运行一个单独的线程来经常触发事件。
  • 我不确定这是否适用,因为很多这些东西都是在运行时完成的。例如。用户可以将 FPS 更改为 30,然后所有后续的跳帧也会更改。当然,步进变量永远不会是恒定的。

标签: performance time modulus processing-efficiency


【解决方案1】:

本质上:

i := 0
WHILE i < n/4
  do rest of stuff × 4
  do stuff that you want to do one time in four
  Increment i

Do rest of stuff i%4 times

这种采用模数并基于它进行切换的变体称为 Duff 设备。哪个更快取决于您的架构:在许多 RISC 芯片上,mod 在单个时钟周期内执行,但在其他 CPU 上,整数除法甚至可能不是本机指令。

如果您本身没有循环计数器,例如,因为它是一个事件循环,您总是可以在执行代码的 if 块中创建一个并每四次重置一次:

i := 1
WHILE you loop
  do other stuff
  if i == 4
    do stuff
    i := 1
  else
    i := i + 1

下面是一个做一些事情的例子,一次做两件事,做一件事情三次:

WHILE looping
  do stuff
  do stuff a second time
  do stuff B
  do stuff a third time
  do stuff C
  do stuff a fourth time
  do stuff B
  do stuff a fifth time
  do stuff a sixth time
  do stuff B
  do stiff C

请注意,您所做的事情可以包括调用一次事件循环。

由于这可能会变得笨拙,您可以使用模板元编程在 C++ 中为您编写这些循环,例如:

constexpr unsigned a = 5, b = 7, LCM_A_B = 35;

template<unsigned N>
  inline void do_stuff(void)
{
  do_stuff_always();
  if (N%a)
    do_stuff_a(); // Since N is a compile-time constant, the compiler does not have to check this at runtime!

  if (N%b)
    do_stuff_b();

  do_stuff<N-1>();
}

template<>
  inline void do_stuff<1U>(void)
{
  do_stuff_always();
}

while (sentinel)
  do_stuff<LCM_A_B>(); 

不过,一般来说,如果您想知道您的优化是否有帮助,请进行分析。

【讨论】:

  • 很有趣,那么为每个跳跃长度使用单独的变量会更快吗?或者即使使用大(100k+ 数字),模数的成本也差不多?
  • 如果添加多个跳过长度,比如 3 和 4,更新两个计数器将花费两倍的时间。您可以保留一个从 1 到 12 运行的计数器,并在它达到 3、6、9 和 12 以及 4、8 和 12 时运行额外的代码,但是您要么有一个 mod,要么有一堆额外的测试。我怀疑每个循环一个 mod 指令的开销在你的 CPU 上会更小。
  • @beta 添加了一个示例,说明如何在没有 mod 的情况下有效地完成此操作,但对于大量数据可能会变得笨拙。 (但总有模板元编程为您完成!)
  • @veta 哎呀,你的名字在那里被自动“更正”了。无论如何,添加了一个模板示例。
  • 是的,这是有道理的,感谢 Lorehead。我需要评估这是否真的是节省开支的最佳方式。我知道我在进行这些算术优化。
【解决方案2】:

答案中最重要的部分:该测试可能只需要很少的时间,在上下文中,离子在你的大脑中移动来考虑它是不值得的。 如果它只花费 1%,那么几乎可以肯定你应该考虑更大的加速。

但是,如果循环速度很快,您可以输入如下内容:

if (--count < 0){
  count = 59;
  // do your thing
}

在某些硬件中,该测试归结为单个指令递减和分支,如果负数。

【讨论】:

  • 是的,我研究了一些架构,它是 1 个单一操作,有些是 3 个。你真的不知道在 Android 上会发生什么,所以我决定把它排除在外,像你一样使用循环/布尔描述。
猜你喜欢
  • 1970-01-01
  • 2013-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多