簡體   English   中英

根據運行時值選擇一個 constexpr 並在熱循環中使用它

[英]choose a constexpr based on a runtime value and use it inside a hot loop

我需要遍歷一個vector ,讀取每個元素,並將 map 取模除法值。 對於 power2 的除數,模除法很快。 因此,我需要在運行時在modmod_power2之間進行選擇。 以下是一個粗略的提綱。 請假設我正在使用模板訪問向量。

位操作技巧取自https://graphics.stanford.edu/~seander/bithacks.html

static inline constexpr bool if_power2(int v) {
  return v && !(v & (v - 1));
}

static inline constexpr int mod_power2(int val, int num_partitions) {
  return val & (num_partitions - 1);
}

static inline constexpr int mod(int val, int num_partitions) {
  return val % num_partitions;
}

template<typename Func>
void visit(const std::vector<int> &data, Func &&func) {
  for (size_t i = 0; i < data.size(); i++) {
    func(i, data[i]);
  }
}

void run1(const std::vector<int> &v1, int num_partitions, std::vector<int> &v2) {
  if (if_power2(num_partitions)) {
    visit(v1,
          [&](size_t i, int v) {
            v2[i] = mod_power2(v, num_partitions);
          });
  } else {
    visit(v1,
          [&](size_t i, int v) {
            v2[i] = mod(v, num_partitions);
          });
  }
}

void run2(const std::vector<int> &v1, int num_partitions, std::vector<int> &v2) {
  const auto part = if_power2(num_partitions) ? mod_power2 : mod;

  visit(v1, [&](size_t i, int v) {
    v2[i] = part(v, num_partitions);
  });
}

我的問題是, run1run2 我更喜歡run2 ,因為它易於閱讀並且沒有代碼重復。 但是,當我在 godbolt ( https://godbolt.org/z/3ov59rb5s ) AFAIU 中檢查兩者時, run1的內聯效果優於run2

那么,有沒有更好的方法在不影響性能的情況下編寫run function?

跟進:

我對此進行了快速測試。 https://quick-bench.com/q/zO5YJtDMbnd10pk53SauOT6Bu0g

看起來對於 clang,沒有區別。 但是對於run1 (10.3),run1 有 2 倍的性能增益。

“問題”與cond? mod_power2: mod cond? mod_power2: mod是一個function指針,比較內聯。

不同的 lambda 沒有共同的類型。 使用諸如std::function類的類型擦除會產生開銷,而且去虛擬化更難優化。

所以,我看到的唯一選擇是能夠以“更好”的方式編寫run1

分解創建lambda; 您需要將mod / mod_power2轉換為仿函數(否則我們會遇到與run2 Demo相同的問題):

void run3(const std::vector<int> &v1, int num_partitions, std::vector<int> &v2) {
  auto make_lambda = [&](auto&& f){
    return [&](std::size_t i, int v){ v2[i] = f(v, num_partitions); };
  };
  if (if_power2(num_partitions)) {
    visit(v1, make_lambda(mod_power2));
  } else {
    visit(v1, make_lambda(mod));
  }
}

演示

如果您不能將mod / mod_power2更改為仿函數,則創建一個仿函數而不是 lambda 以在編譯時獲取它們的地址:

template <int (*f)(int val, int num_partitions)>
struct Functor
{
  std::vector<int> &v2;
  int num_partitions;

  void operator ()(size_t i, int v) const
  {
    v2[i] = f(v, num_partitions);
  }

};

void run4(const std::vector<int> &v1, int num_partitions, std::vector<int> &v2) {
  if (if_power2(num_partitions)) {
    visit(v1, Functor<mod_power2>{v2, num_partitions});
  } else {
    visit(v1, Functor<mod>{v2, num_partitions});
  }
}

演示

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM