优化循环中的函数指针检查

optimize function pointer checking in loop

我有渲染函数,但用户可以通过设置函数指针来覆盖它;在每一帧中,我都在检查它是否存在,如果不存在,我将使用默认函数:

GK_EXPORT
void
gkScenePerLightRenderPath(GkScene *scene) {
  /* ... */

  do {
    /* ... */
    for (i = 0; i < c; i++) {
      modelInst = objs[i];

      if (!scene->renderModelFn)
        gkRenderModel(scene, modelInst);
      else
        scene->renderModelFn(scene, modelInst);
    }
  } while (/* next light */);
}

我想也许我可以这样写:

GK_EXPORT
void
gkScenePerLightRenderPath(GkScene *scene) {
  /* ... */
  GkRenderModelFn renderModelFn;

  /* ... */

  if (!scene->renderModelFn)
    renderModelFn = gkRenderModel;
  else
    renderModelFn = scene->renderModelFn;

  do {
    /* ... */
    for (i = 0; i < c; i++) {
      modelInst = objs[i];
      renderModelFn(scene, modelInst);
    }
  } while (/* next light */);
}

哪个更好?在第二个版本中,我似乎隐藏了 gkRenderModel,所以编译器可能无法应用优化?如果编译器将 gkRenderModel 分配给指针并不重要,那么第二个似乎更优化,因为 if / else 在两个循环之外?

这取决于几个因素:

  • 如果 renderModelFn(或任何其他线程,就此而言)完全修改 scene 对象
  • 如果 gkRenderModel() 声明为 inline 或不声明。

如果它保证是一个外部/外联函数,并且循环中没有任何内容修改scene指针中指向的对象,那么最好把if 循环之前的语句,所以只需要计算一次。

但是,如果被调用函数有可能修改传入 scene 对象的 renderModelFn 成员,则在循环之前移动检查会改变行为。

此外,对函数指针的调用总是越界的。因此,将 if 语句移到循环之外会破坏任何内联的可能性。在这种情况下,最好将 if 语句保留在循环中。