8

それは簡単にわかります。

(i % 3 == 0) && (i % 5 == 0)

次のように簡略化できます。

(i % 15 == 0)

しかし、GCCの出力を調べると、これは高い最適化レベルでも行われていないようです。

これらの種類の最適化を行うコンパイラはありますか、またはこれら2つのテストが意味的に同等ではない正当な理由がありますか?

編集:これはフリンジケースであると言う人々に応えて、以下は同様のケースです:

(i < 3) && (i < 5)

3未満の数値は、常に5未満である必要があります。2番目のテストは冗長です。

また、コンパイラが環境に影響があるかどうかを知ることができないという答えに応えて、次のコードを追加したいと思います...このコードを見てください。

void foo(void)
{
    int i;
    for (i = 0; i <= 10; i++)
    {
        if (i > 20)
        {
            puts("Hi");
        }
    }
}

関数全体は、GCCによって「repzret」に還元され-O2ます。それは私が話している何よりもはるかに複雑です。

4

3 に答える 3

5

これがコンパイラーにとって非常に困難/不可能であると主張するすべてのばかげた答えを無視してください。難しい理由はわかりませんが、おそらく誰もそれをやろうとは思っていなかったか、最適化することが十分に重要だと思っていたのでしょう。これよりも良い答えが必要な場合は、拡張のリクエストとしてGCCバグトラッカーに報告し、開発者の意見を確認する必要があります。

于 2012-04-18T19:43:45.297 に答える
3

あなたの例はかなり単純で、確かに単一の操作に凝縮するのは簡単です。ただし、このようなステートメントの一般的なケースはそれほど単純ではありません。たとえば、次のようにします。

((++i) % 3 == 0) && ((++i) % 5 == 0)

このバリエーションは、単一のモジュラス演算に簡単に単純化することはできません(このステートメントには他のさまざまな問題があることはわかっていますが、単純な変数以外のものを使用している場合、問題はそれほど単純ではないということです。参照)。コンパイラーは通常、ケースに単純な変数のみが含まれていることを確認せず、一般的なケースとは異なる方法で最適化します。可能な限り、最適化の一貫性と予測可能性を維持しようとします。

更新: 質問に追加した追加のケースは、元のコードスニペットとはまったく異なるクラスの最適化に分類されます。到達不能コードであるため、どちらも最適化されており、コンパイル時にそのように証明できます。元の質問には、2つの操作を、元の操作とは異なる1つの同等の操作に書き直すことが含まれていました。追加した2つのスニペットは、既存のコードを書き直さず、実行できないコードを削除するだけです。コンパイラは通常、デッドコードの識別と削除に非常に優れています。

あなたが求めている最適化は、数学的強度低下の一形態です。ほとんどのコンパイラは、ある程度のMSR最適化を提供しますが、どの程度詳細になるかは、コンパイラとターゲットプラットフォームの機能によって異なります。たとえば、モジュラス命令を持たないCPUアーキテクチャを対象とするコンパイラ(代わりに、潜在的に長いライブラリ関数を使用する必要があることを意味します)は、これらのようなステートメントをより積極的に最適化する場合があります。ターゲットCPUがハードウェアモジュラスをサポートしている場合、コンパイラーの作成者は、保存された2つまたは3つの命令が小さすぎて、最適化の改善を実装およびテストするのにかかる時間と労力に見合わないと見なす可能性があります。これは、x86 CPUでは1つの命令で実行できる特定の操作(たとえば)で過去に見たことがありますが、RISCCPUでは数十の命令が必要になります。

于 2012-04-18T19:15:31.593 に答える
1

正直なところ、それは非常に特殊なケースです。方程式のいずれかの部分が変更されると、最適化は適用されなくなります。これはコストと利益の問題だと思います。この最適化を実装するコスト(コンパイル時間の増加、メンテナンスの難しさの増加)は、メリット(まれな状況では動作が少し遅くなります)を上回ります。

一般に、精度の制限とオーバーフローの可能性があるため、数学的リファクタリングは適用できません。ここでは問題ないと思いますが。

于 2012-04-18T19:17:55.463 に答える