36

質問

マンデルブロ フラクタルを計算する簡単なコードをテストしています。点がマンデルブロ集合に属しているかどうかを確認する関数の反復回数に応じて、そのパフォーマンスを確認しています。-fPIC驚くべきことは、フラグを追加した後、時間に大きな違いが生じていることです。私が読んだところ、オーバーヘッドは通常無視でき、私が遭遇した最高のオーバーヘッドは約 6% でした。約 30% のオーバーヘッドを測定しました。アドバイスをいただければ幸いです。

私のプロジェクトの詳細

-O3フラグ、gcc 4.7.2、Ubuntu 12.04.2、x86_64を使用します。結果は次のようになります

    #it​​er C (fPIC) CC/C(fPIC)
    1 0.01 0.01 1.00
    100 0.04 0.03 0.75
    200 0.06 0.04 0.67
    500 0.15 0.1 0.67
    1000 0.28 0.19 0.68
    2000 0.56 0.37 0.66
    4000 1.11 0.72 0.65
    8000 2.21 1.47 0.67
   16000 4.42 2.88 0.65
   32000 8.8 5.77 0.66
   64000 17.6 11.53 0.66

私が使用するコマンド:

gcc -O3 -fPIC fractalMain.c fractal.c -o ffpic
gcc -O3 fractalMain.c fractal.c -o f

コード: fractalMain.c

#include <time.h>
#include <stdio.h>
#include <stdbool.h>
#include "fractal.h"

int main()
{
    int iterNumber[] = {1, 100, 200, 500, 1000, 2000, 4000, 8000, 16000, 32000, 64000};
    int it;
    for(it = 0; it < 11; ++it)
    {
        clock_t start = clock();
        fractal(iterNumber[it]);
        clock_t end = clock();
        double millis = (end - start)*1000 / CLOCKS_PER_SEC/(double)1000;
        printf("Iter: %d, time: %lf \n", iterNumber[it], millis);
    }
    return 0;
}

コード: fractal.h

#ifndef FRACTAL_H
#define FRACTAL_H
    void fractal(int iter);
#endif

コード: fractal.c

#include <stdio.h>
#include <stdbool.h>
#include "fractal.h"

void multiplyComplex(double a_re, double a_im, double b_re, double b_im, double* res_re, double* res_im)
{
    *res_re = a_re*b_re - a_im*b_im;
    *res_im = a_re*b_im + a_im*b_re;
}

void sqComplex(double a_re, double a_im, double* res_re, double* res_im)
{
    multiplyComplex(a_re, a_im, a_re, a_im, res_re, res_im);
} 

bool isInSet(double P_re, double P_im, double C_re, double C_im, int iter)
{
    double zPrev_re = P_re;
    double zPrev_im = P_im;
    double zNext_re = 0;
    double zNext_im = 0;
    double* p_zNext_re = &zNext_re;
    double* p_zNext_im = &zNext_im;
    int i;  
    for(i = 1; i <= iter; ++i)
    {
        sqComplex(zPrev_re, zPrev_im, p_zNext_re, p_zNext_im);
        zNext_re = zNext_re + C_re;
        zNext_im = zNext_im + C_im;
        if(zNext_re*zNext_re+zNext_im*zNext_im > 4)
        {
            return false;
        }
        zPrev_re = zNext_re;
        zPrev_im = zNext_im;
    }
    return true;
}

bool isMandelbrot(double P_re, double P_im, int iter)
{
    return isInSet(0, 0, P_re, P_im, iter);
}
void fractal(int iter)
{
    int noIterations = iter;
    double xMin = -1.8;
    double xMax = 1.6;
    double yMin = -1.3;
    double yMax = 0.8;
    int xDim = 512;
    int yDim = 384;
    double P_re, P_im;
    int nop;
    int x, y;

    for(x = 0; x < xDim; ++x)
        for(y = 0; y < yDim; ++y)
        {
            P_re = (double)x*(xMax-xMin)/(double)xDim+xMin;
            P_im = (double)y*(yMax-yMin)/(double)yDim+yMin;
            if(isMandelbrot(P_re, P_im, noIterations))
                nop = x+y;
        }
        printf("%d", nop);
}

比較の裏話

実行可能ファイルをビルドするときにフラグを追加するのは少し人工的に見えるかもしれません-fPIC(コメントの 1 つに従って)。説明のいくつかの言葉: 最初に、プログラムを実行可能としてコンパイルしただけで、C から isMandelbrot 関数を呼び出す Lua コードと比較したかったので、lua から呼び出す共有オブジェクトを作成しましたが、大きな時間差がありました。しかし、なぜそれらが反復回数とともに成長しているのか理解できませんでした。結局、それが原因であることがわかりました-fPIC。lua スクリプトを呼び出す小さな C プログラムを作成する場合 (実質的に同じことを行いますが、.so は必要ありません) - 時間は C (なし-fPIC) と非常に似ています。そのため、ここ数日間、いくつかの構成で確認しましたが、2 つの非常によく似た結果が一貫して示されています。-fPICそしてそれで遅くなります。

4

3 に答える 3

51

-fPICオプションなしでコンパイルするとmultiplyComplex、 、sqComplexisInSetおよびisMandelbrotがコンパイラによって自動的にインライン展開されることがわかります。これらの関数を静的として定義すると-fPIC、コンパイラがインライン化を自由に実行できるため、コンパイル時に同じパフォーマンスが得られる可能性があります。

コンパイラがヘルパー関数を自動的にインライン化できない理由は、シンボルの挿入に関係しています。すべてのグローバル データに間接的に、つまりグローバル オフセット テーブルを介してアクセスするには、位置に依存しないコードが必要です。プロシージャ リンク テーブルを通過する必要がある関数呼び出しにも、まったく同じ制約が適用されます。シンボルは実行時に別のシンボルによって挿入される可能性があるため (「参考文献」を参照LD_PRELOAD)、コンパイラは、グローバルな可視性を持つ関数をインライン化することが安全であると単純に想定することはできません。

なしでコンパイルした場合も、まったく同じ仮定を行うことができます-fPIC。つまり、コンパイラは、実行可能ファイルで定義されたグローバル シンボルを挿入できないと安全に仮定できます。ルックアップ スコープが実行可能ファイル自体で始まり、プリロードされたものを含む他のすべてのライブラリがその後に続くためです。もの。

より完全な理解については、次の論文をご覧ください。

于 2013-04-08T22:46:51.573 に答える
2

冒頭の投稿のコメント セクションで他の人が議論しているように、-fltogcc のリンク時間の最適化により、実際にインライン化しても問題ないことがわかる可能性が高いため、コンパイルすると、この特定のケースで見られる実行時間の差を減らすのに役立つはずです。いくつかの機能;)

一般に、リンク時間の最適化は、コード サイズ (最大 6%) の大幅な削減につながる可能性があります。リンク時間の最適化で紙にリンクし、実行時間も短縮します (より多くのプログラムがキャッシュに収まります)。-fPICまた、ほとんどの場合、より厳格なセキュリティを有効にする機能と見なされ、 android で常に有効になっていることに注意してください。SOに関するこの質問でも簡単に説明します。また、-fpicは の高速バージョンであるため、代わりにtryを-fPIC使用する必要がある場合は、 gcc docs にリンクしてください。x86 の場合は違いがないかもしれませんが、これを自分で確認するか、gcc-help で質問する必要があります。-fPIC-fpic

于 2016-03-30T23:01:59.927 に答える