---
# System prepended metadata

title: Multi Versioning
tags: [elkurin-daily-note, C++-General]

---

# Multi Versioning

Multi VersioningはGCCやclangの機能で、ある関数を複数バージョン作りそれぞれのターゲットを指定できる。  
ここで言うターゲットとは、例えば sse4.2, `arch=goldmont` のようなアーキテクチャ。

## target
GCCでは[`target`](https://gcc.gnu.org/onlinedocs/gcc/Function-Multiversioning.html)というattributeを使う。  
例えば、`foo`という関数をいくつかのターゲットに対して定義すると以下のようになる。
```cpp=
__attribute__ ((target ("default")))
int foo() {
  return 0;
}

__attribute__ ((target ("sse4.2")))
int foo() {
  return 1;
}

__attribute__ ((target ("arch=goldmont")))
int foo() {
  return 2;
}

// ターゲットごとに違うやつを使ってくれる
foo();
```
テンプレートのノリ。"sse4.2" などの特殊版にマッチするアーキテクチャはそれを使い、一致しない場合は"default"にフォールバック。  
もし複数にマッチする場合は決められたPriorityに則ってdispatchされる。

### Priority について
インストラクションセットの順だとわかりやすく新しい方が優先順位が高い。  
AVX2 > AVX > POPCNT > SSE4.2 > ...  
また`arch=*`系との優先順位については以下のようなルールになる。

`arch=<processor X>`(e.g. goldmont)と`<ISA Y>`(e.g. SSE3) のバージョンがあったとする。  
ISA群の中でXがサポートされている最高優先度のやつとYを比較してより高い方を取る。

参考：[GCC Wiki](https://gcc.gnu.org/wiki/FunctionMultiVersioning#Dispatch%20Priority)

## cpu_specific
一方Clangでは[`cpu_specific`](https://clang.llvm.org/docs/AttributeReference.html#cpu-dispatch)というattributeを使う。  
雰囲気はtargetとだいたい同じ。

```cpp=
__attribute__ ((cpu_specific(ivybridge)))
int foo() {
  return 0;
}

__attribute__ ((cpu_specific(atom)))
int foo() {
  return 1;
}

__attribute__ ((cpu_specific(genetic, sandybridge)))
int foo() {
  return 2;
}

// ターゲットごとに違うやつを使ってくれる
foo();
```

`cpu_specific`に渡す引数でCPU名を指定できる。  
複数のCPU名をリストアップすることも可能。  

## 用途
わかりやすいのだと、アーキテクチャごとに最適な実装が違うときに、一部のボトルネックになる関数だけそれぞれの最適実装を用意しておいてあげることで、比較的低コストでパフォーマンス改善が見込める。