1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Apple M1 に嫌がらせをする(80bit 浮動小数点数)

Last updated at Posted at 2022-02-23

これは何?

x86 は、32bit, 64bit の他に 80bit の浮動小数点数を計算する装置を内蔵していて、いずれもめちゃくちゃ速く計算できる。

一方。
Apple M1 は 80bitの浮動小数点数を計算する装置は持っていないので、80bit の浮動小数点数を計算するにはそうとう面倒なことをする必要がある。

じゃあさせてみよう、というのがこの記事の趣旨。

前提

C/C++ には普通三種類の浮動小数点数型がある。gcc なんかでは下表のとおりになっている。

x86 / amd64 でのサイズ Apple M1(arm64) でのサイズ
float 32 32
double 64 64
long double 80 1 64

なので、M1 上でネイティブコードを動かしても 80bit では計算できない。
そこで、amd64 用の 80bit の浮動小数点数をつかうバイナリを作り、Rosetta2 を使って Apple M1 上で動かす。

実験

環境

マシンは手元にある下記の2台を使った。

  • MacBook Pro 14 inch (M1 非Max)
  • MacBook Pro (Core i7, Mid 2015)

ソースコード

そしてソースコード。

c
# include <stdio.h>
# include <stdlib.h>
# include <sys/time.h>

typedef NUM_T num_t;

long double test(int n)
{
    num_t sum = 0;
    num_t const one = 1.0;
    for (int i = 1; i < n; ++i)
    {
        sum += (one / (i+1))*(one / i);
    }
    return sum;
}

int main(int argc, char const *argv[])
{
    struct timespec start={0}, end={0};
    clock_gettime(CLOCK_REALTIME, &start);
    long double r = test(20000000);
    clock_gettime(CLOCK_REALTIME, &end);
    time_t ds = end.tv_sec - start.tv_sec;
    time_t dn = end.tv_nsec - start.tv_nsec;
    printf("sizeof(num_t) = %zu\n", sizeof(num_t));
    printf("r=%.20Lf t=%f\n", r, ds + dn * 1e-9);
    return 0;
}

除算・乗算・加算 を使った無駄な計算。

NUM_T は、コンパイラオプションで定義するんだけど、 float, double, long double のいずれか。

コンパイルオプション

コンパイルオプションは以下の通り。

全種類共通として -O2, -std=c17, -fexcess-precision=standard を指定。
あとは下表。

設定名 オプション
native なし
sse -mfpmath=sse -msse2
387 -mfpmath=387

実行結果

まずグラフ。縦軸は実行時間なので、上ほど遅い。縦軸対数目盛注意。

image.png

時間はグラフのとおりなんだけど、計算結果についてもひとつ。

double の場合、-mfpmath=387-mfpmath=sse -msse2 では計算結果が異なる。計算途中で 80bit を使うかどうかが違うので、差が出てしまう。下表のとおり。

設定名 浮動小数点関連オプション 計算結果
native なし r=0.99999995000372399190
sse -mfpmath=sse -msse2 r=0.99999995000372399190
387 -mfpmath=387 r=0.99999995000375718757

このあたりの話は 浮動小数点数の呪われた世界(x87 と C/C++) に書いた。

float でも差が出そうなものなんだけど、今回は差が出なかった。

感想とか

80bit 浮動小数点数の計算を Apple M1 でやるととても遅いことが確認できた。64bit と 80bit では、100倍以上遅い。

ソースコード上は long double を使っていなくても、 -mfpmath=387 をつけてしまうと途中の計算で 80bit 浮動小数点数を使ってしまうため、とても遅くなる。

float と double で、Apple M1 native (arm64 native) よりも rosetta (arm64 sse) の方が速いのは、Apple M1 向けだとコンパイラの最適化器が頑張りきれてないということだと思う。あるいは実行時最適化が走ったりするとか?

あと。
こういうときって何グラフを使うのがいいんだろう。
縦軸が対数目盛なので棒グラフは避けたい。
横軸がハードウェアとかコンパイラオプションなので折れ線グラフもありえない。

仕方ないので散布図にしたんだけど、なんか寂しい絵だなと思って。
ふつうどうするんだろ。


  1. sizeof(long double) は 16 なので 128bit あるが、意味のあるデータは 80bit。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?