C ++アプリケーションのパフォーマンスの最適化(3) - パフォーマンス分析(1)C ++言語の機能

C ++アプリケーションのパフォーマンスの最適化(3) - C ++言語は、パフォーマンス分析が特徴

、C ++言語は、パフォーマンス分析の概要が特徴

通常、ほとんどの開発者はアセンブリ言語とC言語プログラムの非常に高い性能要件を製造するためのより適切な、と思いますが、C ++言語は主に複雑の調製に使用されるプログラムの非常に高いではなく、非常に高い性能要件です。ほとんどの開発者が考えるときには、それによって、あまりにも多くの新しい言語機能を導入し、複数の(例えば、オブジェクト指向プログラミングやジェネリックプログラミングなど)プログラミング・モデルと同様に例外処理をサポートし、C ++言語のデザインを考えているので。C ++コンパイラの特性は、余分なコードの多くは、プログラムをコンパイルするときになるように挿入された新しい言語は、最終体積膨張を引き起こすバイナリコードを生成し、実行速度が低下します。

しかし、それは通常、およそプログラムの速度は、フレームワーク設計で識別されていないたスピードにつながったC ++言語の使用が期待を満たしていなかったので、完全な、とではありません。このため、プログラムのパフォーマンスを改善する必要性は、それを行うには、あなた最初必要がクリティカルパスと実際のパフォーマンスのボトルネックを特定するために、その実行時間分布の正確な測定のためのパフォーマンステストツールを使用することで、その後、パフォーマンスのボトルネックを分析し、最適化ではなく、主観的には使用され、パフォーマンスの問題言語プログラムを考えました。エンジニアリングの実践は、フレームのデザインが変更されない場合、でもC言語やアセンブリ言語の書き換えを使用して、またそれが全体のパフォーマンスを向上させる保証するものではない、ということを示しています。

したがって、あなたは、パフォーマンスの問題、最初のチェックを経験し、プログラムの全体的なアーキテクチャを反映して、正確な測定を行うために、実際の動作パフォーマンステストツールを使用して、分析し、パフォーマンスのボトルネックのために最適化されています。

589348389、:あなたは、C ++プログラマを学びたいと思っている場合は、私たちのC / C ++学習バックルqunに来ることができる
無料配達C ++ビデオチュートリアルああ!
各Iは、C / C ++の知識を説明するためにグループに住んでます20:00、ああを学ぶために皆を歓迎します。
 

C ++言語は、他の要因よりもパフォーマンスのボトルネックの手順になる可能性が高いいくつかの動作特性を持っている次のようにしかし、一般的な要因は以下のとおりです。

(1)欠落しているページ

ページが表示されないことは、通常に関して、外部メモリ・アクセスがメモリまたはコードの実行、大きさの差の順にアクセスするため、外部ストレージにアクセスすることを意味します。そのため、可能な限り、不足しているページを軽減する方法を見つけるようにしてください。

(2)スタックから動的にメモリを解放します

C言語のmalloc /無料のC ++言語と新しい/削除操作はとてもできるだけスレッド・スタック・メモリからの優先権を取得するには、非常に時間がかかります。ダイナミックヒープを削減し、メモリ・アプリケーションスタック、スタックに割り当てられたヒープメモリ理由ははるかに遅いよりだけでなく、関連欠落しているページを最小限に抑えることを優先。肯定的な物理メモリに配置プログラムが実行され、現在のスタックフレームメモリページ空間、プログラムコード変数アクセスは、ページフォルトが発生しないステップと、スタック上のオブジェクトへのポインタのみ場合にヒープ領域からオブジェクトを生成し、オブジェクト自体がヒープ領域に格納されます。物理メモリ内のスタック、およびヒープメモリ割り当ての特性は、たとえ隣接する二つのオブジェクトが最も可能性が高い離れヒープメモリ位置に、発生する可能一般的ではありません。そのため、2つのオブジェクトがアクセスされたとき、2オブジェクトへのポインタであるが、スタック上にあるが、2つのオブジェクトが2つのポインタによって参照すると、ページフォールトを引き起こす可能性があります。

(3)複合オブジェクトの作成および破壊

再帰呼び出しのより深いレベルが再帰的に作成内のオブジェクトに焦点を当てる必要があるためので、複雑なオブジェクトや破壊は、より多くの時間がかかるだろう作成します。第二に、ソースプログラムに見られるようにコンパイラが生成した一時オブジェクトはより少ないが、検出することは容易ではないので、集中する必要があります。

(4)関数呼び出し

関数呼び出しオーバーヘッドが固定されているので、そのように機能コード量は比較的小さく、非常にしばしば関数が呼び出されたときに、関数呼び出しオーバーヘッド容易に不要なオーバーヘッドとなる固定されている場合。ワンC言語やC ++言語のインライン関数は、関数のモジュラー性質における関数呼び出しのオーバーヘッドをなくすために固定された導入を維持することに基づいて呼び出します。C言語のマクロ以来だけでなく、エネルギーの利点×××における開発およびデバッグ不便にするため、C ++言語のインライン関数を使用することをお勧めします。

第二に、コンストラクタとデストラクタ

1、コンストラクタとデストラクタの紹介

オブジェクトのコンストラクタを作成する際に機能コンストラクタとデストラクタが自動的に実行され、オブジェクトが破棄される場合、デストラクタが自動的に実行されます。コンストラクタが最初に実行されるオブジェクトの関数、呼び出しはオブジェクトを作成する際に、オブジェクトの初期状態を初期化し、ファイル、ネットワーク接続などの所望のオブジェクトの前に使用されるいくつかのリソースにアクセスすることで、オブジェクトのデストラクタが最後であります関数が実行され、オブジェクトは解放のためのリソースを持っています。オブジェクトの有効期間では、コンストラクタとデストラクタは一度だけ実行されます。

二つの方法でオブジェクトを作成し、1はローカルオブジェクトと呼ばれるスタックを実行中のスレッドから作成されます。ローカルオブジェクトが破壊されているデストラクタがオブジェクトを自動的に起動され属するプログラムはデストラクタを呼び出しますが、プログラムがオブジェクトの範囲を使い果たしたときに表示する必要はありません。

もう一つの方法は、動的グローバルヒープから割り当てられたオブジェクトを作成することで、通常、新規またはmallocのヒープ領域を割り当てます。

Obejctの* pを=新しいオブジェクト(); // 1 
//何か// 2やる
削除pは; // 3 
のp = NULL; // 4

ステートメントが実行されると、ポインタPの指示物体をグローバルメモリヒープ領域から得られ、そしてPに割り当てられたアドレスされ、P自体はローカル変数であり、スレッドスタックから割り当てられる必要がある、pが割り当てられたグローバルヒープメモリから指示物体でありますストレージ。破壊のために作成されたグローバルヒープオブジェクトから、削除するオブジェクトへの呼び出しにデストラクタポインタpポイントを削除コール表示すると、オブジェクトによって占有グローバルヒープのメモリ空間は、グローバルヒープに返されます。文3を実行した後、オブジェクトへのポインタp点が破壊されるが、プログラムは、その範囲内に出るまで、ポインタPは、スタック内に存在します。オブジェクトの破壊は、Pによって指された後に、p個のポインタは依然として、破壊次にポインタPがダングリングポインタとなるオブジェクトのグローバルヒープ領域の位置を指し示すポインタPを使用して、この時間は、危険な、通常推奨P割当NULLです。

Win32プラットフォームでは、オブジェクトのグローバルな破壊がメモリ空間をヒープにアクセスすると、3つの状況につながることができます:

(1)対象は問わない場合は、ヒープマネージャが占有オペレーティングシステムのヒープ領域のさらなる回復を持って破棄されるメモリページを、ポインタにアクセスして、この時間は、プロセスがクラッシュする原因、アクセス違反、すなわち不正なメモリアクセスを引き起こす可能性があります。

(2)メモリ・ページは、このときポインタPアクセスの値を取ることによって、他のオブジェクトの存在、およびオブジェクトが一度他のオブジェクトに割り当てられていない回収された後、グローバルヒープ領域によって占有破壊されたオブジェクトを破壊しているとはいえないが、無意味ですすぐにクラッシュコースを引き起こしたが、その後の運転行動のためのポインタpは予測不可能です。

(3)オブジェクトがメモリページが他のオブジェクトであり、オブジェクトは、ポインタp他のオブジェクトの値によって、この時間が達成されると破壊の他のオブジェクトに割り当てられたグローバルヒープを回収した後の空間を占有されて破壊された場合、もののポインタpアクセスは、プロセスの崩壊を引き起こすが、オブジェクトの状態の変化を引き起こす可能性がありません。

図2に示すように、プロセスオブジェクトの構成

まず、所望のオブジェクトメモリ(スレッドスタックまたはグローバルからヒープ)を得、オブジェクトを2つの段階に分割されて作成し、メモリ空間内のコンストラクタを実行します。オブジェクトコンストラクタを構築する場合、コンストラクタは、2つの段階に分かれています。初期化の最初のステップが実行され、第二段階は、コンストラクタ(初期化パラメータリスト)の実行体です。

派生クラス:公共ベース
{ 
パブリック:
    派生():// 1のid(1)、名前( "名前")
    { 
        //何かを行う// 2 
    } 
:プライベート
    int型のID。
    文字列名。
}。

結腸内のコードステートメントは、初期化リスト1であり、各セルは、異なるユニット間のカンマで区切られた、可変のモード名(値)に初期化されます。コンストラクタは、第1の初期化リストに従って初期化を行い、その後、コンストラクタ(文2)の本体を実行します。次のように初期化動作があることに注意してください:

(1)コンストラクタは、各再帰内部の動作は厳密な順序に従って、実際に再帰的操作です。再帰モードが第1のコンストラクタ親クラス(親クラスのコンストラクタの動作はそれに応じて初期化を行うとコンストラクタ本体実行2つの部分を含む)が行われ、親クラスのコンストラクタは、クラス自体の可変構成部材を返します。自分のクラスのメンバ変数を構築する場合、1は、クラス宣言の順に厳密に従ってメンバ変数であり、そしてメンバ変数の初期化リスト中の出現の順序は何の関係もありません。親オブジェクトまたは変数の一部のメンバーは、初期化リストに表示されない場合に、第2がありますまだ初期化操作で初期化するとき、ビルトイン型のメンバ変数を初期値に割り当てられ、親オブジェクトのクラスのメンバ変数オブジェクトはデフォルトコンストラクタが初期化呼び出して、その後、親クラスのメンバ変数のコンストラクタと子オブジェクトコンストラクタの実行中にも、上記の再帰に従って、すべての親クラスと親クラスが工事の完成に含まれるまで、クラスの継承階層のメンバ変数は、クラスの初期化動作が完了します。

(2)親オブジェクトと初期化リストのメンバ変数の数が表示されない、それはまだデフォルトコンストラクタを実行されます。このように、オブジェクトのクラスは、対応するクラスの要件が明示的にデフォルトコンストラクタを提供しなければならない、またはコンパイラは、外の他のタイプに加えて、デフォルトコンストラクタが暗黙的に定義されたデフォルトのコンストラクタを生成し、防ぐことができないため、適切なデフォルトコンストラクタが呼び出すことができます提供しなければなりませんコンストラクタは、コンパイラが生成するデフォルトコンストラクタを防ぐことができます。コンパイル時にコンパイラは、デフォルトコンストラクタが求めていないことがわかり、およびコンパイラがデフォルトコンストラクタを生成できない場合は、コンパイラはできません。

(3)メンバ変数の二つのタイプ、その(すなわち、一定の基準タイプ及びタイプ)を強調することが必要です。変数のすべてのメンバーが構成されているので、すなわち、関数を実行する前に、既に一定と参照型変数が適切に初期化リストその中に初期化されなければならないため、したがって、その初期値を有するが、生体内を初期化しないコンストラクタです。

(4)初期化リストは、その子メンバーや親オブジェクトのメンバーの完全なリストではないかもしれない、またはクラスの宣言の異なる順序がまだ完全になると厳密に厳密な順序が構築されていることを確認します。すなわち、身体に入る前に、コンストラクタ手順は、親オブジェクトクラスのメンバ変数とすべての子オブジェクトが生成され、設定されています。コンストラクタ本体での割り当てを実行する場合は、明らかに無駄に属します。コンストラクタで、すでにクラスのサブメンバ変数を初期化する方法を知っていれば、あなたは、コンストラクタ本体で初期化されていない、可変の子メンバーのコンストラクタ初期化子リストによって与えられた情報を初期化する必要があるコンストラクタになって、子メンバ変数理由これは、一度初期化されています。

図3に示すように、オブジェクトの破壊のプロセス

コンストラクタとデストラクタは、再帰的なプロセスですが、異なるがあります。初期化動作のデストラクタ、デストラクタ主要な作業が行われるデストラクタ関数本体の一部が存在しない、二つの対向再帰コンストラクタデストラクタ行わ、各層の再帰を、オブジェクトのメンバ変数デストラクタは、シーケンスコンストラクタを逆転させること。

クラス宣言デストラクタの順序として、順番に(正のシーケンス又は逆方向)の基準を選択唯一のクラスメンバー変数をデストラクタ。正シーケンスコンストラクタを選択するので、むしろデストラクタコンストラクタ、デストラクタこうして選択逆に動作。そして、デストラクタメンバ変数は、基本デストラクタ配列(正のシーケンス又は逆)としてクラス宣言順序でのみ使用することができ、したがって構成のみの番号順としてクラスのメンバ変数宣言のためにコンストラクタを選択することができるので、注文は、注文に応じて初期化リストとして使用することができません。

オブジェクト操作エンドノードは、複雑な継承のシステムに属している場合は、そのデストラクタは非常に時間のかかるプロセスとなります。

C ++プログラムでは、作成と破棄オブジェクトは非常に著名な操作のパフォーマンスに影響を与えます。まず、オブジェクトがグローバルヒープ空間から生成された場合には、メモリブロックのサイズに一致するように求めている伴うため、動的なメモリ割り当て操作の必要性、動的メモリ割り当てと回復は、非常に時間のかかる作業であるにも見つける必要があるかもしれません切捨て処理は、その後もグローバルヒープメモリ使用状況の情報を維持するために、リストを変更する必要があります。頻繁なメモリ操作は真剣に、グローバル動的ヒープメモリ空間からアプリケーションの数を減らすことができ、メモリ・プールの技術を使用し、パフォーマンスの低下に影響を与えるプログラムの全体的なパフォーマンスが向上します。取込みメモリは、オブジェクトを生成するために、必要に応じて複雑な継承階層端末のクラスに属する場合、コンストラクタは再帰的な構造は、そのような多数のオブジェクトがCPU設定動作を消費する大規模で複雑なシステムにおける一連の動作を引き起こすであろうと呼ばれ主なセクション。

コンパイラが一時的に発生したオブジェクトの作成と破壊がパフォーマンスに影響しますので、同時に自分自身のコード生成ターゲットを最小限に抑える、我々はオブジェクトに焦点を当てる必要がありますコンパイル時に、一時的なオブジェクトの生成を避けるようにしてください。

実施コンストラクタは、身体のコンストラクタに第二の割り当てを実行する場合、それはまた、CPU時間の無駄です。

図4に示すように、伝達関数パラメータ

オブジェクト作成および破壊を減少させる一般的な方法は、すべてのような、参照によって渡された宣言で、定数の代わりに値によって渡されます。

INTのFUNC(オブジェクトobj); // 1つ
のint FUNC(constオブジェクト&OBJ); // 2

次のように値が検証例を渡さ:

書式#include <iostreamの> 

名前空間stdを使用。

クラスObject 
{ 
パブリック:
    オブジェクト(I = 1 INT)
    { 
        N = I。
        裁判所未満<< "オブジェクト(int型は、I = 1):" <<てendl; 
    } 
    オブジェクト(constオブジェクトと別の)
    { 
        N = another.n。
        裁判所未満<< "オブジェクト(constオブジェクト&別):" <<てendl; 
    } 
    ボイド増加()
    { 
        N ++。
    } 
    int値()constは
    { 
        戻りN。
    } 
    〜オブジェクト()
    { 
        COUT << "〜オブジェクト()" << ENDL。
    } 
プライベート:
}。
ボイドFUNC(オブジェクトobj)
{ 
    COUT << "FUNCを入力して、増加前()、N =" << obj.value()<< ENDL。
    obj.increase(); 
    COUT << "FUNCを入力し、増加後()、N =" << obj.value()<< ENDL。
} 
メインINT()
{ 
    オブジェクト。// 1 
    COUT << "コールFUNC前に、N =" << a.value()<< ENDL。
    FUNC(A)。// 2 
    COUT << "コールFUNC後、N =" << a.value()<< ENDL; // 3 

    リターン0。
} 
//出力:
// 4://オブジェクトは、(i = 1 INT)
//コールFUNC前に、N = 1つの
//オブジェクト(constオブジェクト&別):// 5 
//増加する前に、FUNCを入力し()、
//コールFUNC後、N = 1 // 9 
//〜オブジェクト()

図4は、文の目標出力文構造1であり、ステートメントが出力FUNC 5(a)は関数呼び出しでオブジェクトを呼び出すときに発生する文2は、n個のチェック機能、続いてコピーコンストラクタコピーによって開始されます次に、出力文6、同じ出力値、及び外部要素オブジェクトFUNC関数の出力値は、コピー機能がNに1を加え、次いで、2つのn個のレプリカの値、および出力ステートメント7の値を大きく呼び出します。コピー、出力文8の破壊の実施後FUNC機能。n個の印刷元の目標値の主な機能は、出力文9、1であり続けます。

パラメータに渡された関数宣言がオブジェクトである場合、この関数は、入ってくるパラメータを変更していない場合、機能が実現するように設計することができるが、それは不必要な複製を生成します;関数のパラメータ渡しを変更する必要性は、それが参照パラメータで渡されるべきとき対象製品は、不要な構成と破壊操作を導入、基準は、一定のパラメータを使用して渡さなければなりません。

性能検証例に繰り返し割り当てコンストラクタへの影響は以下のとおりです。

書式#include <iostreamの> 
の#include <TIME.H> 

std名前空間を使用しました。

クラスDArray 
{ 
パブリック:
    DArray(ダブルV = 1.0)
    { 
        ため(; I 1000 <I ++は、I = 0 INT)
        { 
            D [I] = V + iは、
        } 
    } 
    ボイドのinit(ダブルV = 1.0)
    { 
        (INT I = 0、I 1000 <; I ++)のために
        { 
            D [I] = Vを+ I。
        } 
    } 
プライベート:
    ダブルD [1000]; 
}。

クラスObject 
{ 
パブリック:
    オブジェクト(ダブルV)
    { 
        d.init(V)。
    } 
プライベート: 
    DArray dを、
}。

メインINT()
{ 
    起動clock_t、仕上がり。
    )(=クロックを開始します。
    以下のために(; I <100000 iは++ i = 0 INT)
    { 
        オブジェクトobj(+ I 2.0)。
    } 
    仕上げ=クロック()。
    裁判所未満<< "使用時間:" <<ダブル(終了-開始)<< "" <<てendl; 

    0を返します。
}

次のようにメンバ変数を初期化リストで初期化されている場合、60万台を消費し、そのコードは次のとおりです。

書式#include <iostreamの> 
の#include <TIME.H> 

std名前空間を使用しました。

クラスDArray 
{ 
パブリック:
    DArray(ダブルV = 1.0)
    { 
        ため(; I 1000 <I ++は、I = 0 INT)
        { 
            D [I] = V + iは、
        } 
    } 
    ボイドのinit(ダブルV = 1.0)
    { 
        (INT I = 0、I 1000 <; I ++)のために
        { 
            D [I] = Vを+ I。
        } 
    } 
プライベート:
    ダブルD [1000]; 
}。

クラスObject 
{ 
パブリック:
    オブジェクト(ダブルV):D(V)
    { 
    } 
プライベート: 
    DArray dを、
}。

メインINT()
{ 
    起動clock_t、仕上がり。
    )(=クロックを開始します。
    以下のために(; I <100000 iは++ i = 0 INT)
    { 
        オブジェクトobj(+ I 2.0)。
    } 
    仕上げ=クロック()。
    裁判所未満<< "使用時間:" <<ダブル(終了-開始)<< "" <<てendl; 

    0を返します。
}

30万台を消費し、性能が約50%向上しています。

第三に、継承と仮想関数

図1に示すように、仮想関数と動的結合機構

仮想関数は、C ++言語で導入された重要な機能である、比較的明確になるように動的バインディングメカニズム意味クラスを継承することを動的バインディングメカニズムを提供します。

(1)一般的な抽象ベースクラスデータと動作を制御します。各派生クラスのデータメンバを使用する必要がある場合、データのために、それは基底クラス内で宣言される必要があり、操作言語のために、オペレータは各派生クラスの意味を有する場合、関係なく改変及びセマンティクスされるかどうかの拡大は、基底クラス内で宣言する必要があります。

(2)特定の操作は、各派生クラス、完全な意味的な整合性、および変更することなく拡張するために、対応する動作が非仮想基本クラスのメンバ関数宣言されます。デフォルトのベース・クラス・データ・メンバーは、コードの再利用を構成する任意の追加のステートメントを行うことなく、として継承された場合には、各派生クラス宣言は、ベースから派生したクラス、およびデフォルトのステートメント実装継承非仮想メンバ関数です。

各派生クラスの意味を有するが、セマンティクスが同一ではないものの、(3)いくつかの操作では、操作は、仮想メンバ関数に対応するものとして宣言されなければなりません。各派生クラスは、仮想メンバ関数を実装する必要があり、意味的に宣言し、仮想メンバ関数の実装を継承するが、が変更または延長されます。派生クラスで修正、拡張、仮想メンバ関数の実装プロセスは、追加の独自のデータを使用する必要がある場合は、該当するデータは、独自のデータメンバーの派生クラスとして宣言されています。

より高いレベルのフレームワーク(継承されたユーザシステム)は、オブジェクトのコレクションの抽象化のレベルを扱う、この継承システムを使用すると、オブジェクトの性質のメンバーは、様々な派生クラスオブジェクトのコレクションであるが、オブジェクトのコレクションのプロセス内のオブジェクト、操作の抽象化のレベルを使用します。各操作の実際の実行は、ランタイムシステムは、動的バインディングを使用する必要がある操作を識別することが可能であるときに動作が、異なるされた操作に対応する操作が同じ派生クラスであるハイレベルのアプリケーション・フレームワーク、派生クラスを区別しません。派生クラスに対応した修正または拡張解除操作を見つけるために。継承システム内の多様性の面でユーザーの継承階層が透明であり、その後継者の詳細を心配する必要はありません、プロセスが全体目標と整合的にユーザーの行動のセットです。内部継承システムが増加すると、派生クラス、または変更を達成するために、派生クラスの仮想関数を削除しても、ユーザーがコードを変更する必要はありません、プログラムのモジュール性が大幅に向上し、そのスケーラビリティされています、コードの可読性と保守性が向上します。オブジェクトの場合のみ、特定の特にどのような種類を心配することなく、抽象型を参照するために、システムのユーザーを継承します。

589348389、:あなたは、C ++プログラマを学びたいと思っている場合は、私たちのC / C ++学習バックルqunに来ることができる
無料配達C ++ビデオチュートリアルああ!
各Iは、C / C ++の知識を説明するためにグループに住んでます20:00、ああを学ぶために皆を歓迎します。
 

おすすめ

転載: blog.csdn.net/XZQ121963/article/details/91431758