コードの作成方法の段階的なシフトは、システムのパフォーマンスに影響しましたか?そして、私は気にする必要がありますか?


35

TD; DR:

私が尋ねていたものに関していくつかの混乱があったので、ここに質問の背後にある運転のアイデアがあります:

私はいつもそれが何であるかという質問を意図していました。当初はうまく表現できなかったかもしれません。しかし、意図は常に「モジュール式、分離、疎結合、分離、リファクタリングされたコード」であり、「モノリシックな単一ユニット、1か所ですべてを行う、1つのファイル、密結合された」コードよりも、それ自体の性質が著しく遅い。残りは単なる詳細であり、その際に私が出会った、または現在または将来出会うであろうさまざまな症状です。ある程度の規模では確かに遅いです。最適化されていないディスクのように、どこからでも断片を拾わなければなりません。遅いです。確かに。しかし、私は気にする必要がありますか?

そして問題は…ではない

マイクロ最適化、時期尚早な最適化などに関するものではありません。「これまたはその部分を最適化して死ぬ」ことではありません。

それは何ですか?

それは、時間の経過とともに現れたコードの記述についての全体的な方法論と手法、および考え方に関するものです。

  • 「このコードを依存関係としてクラスに挿入する」
  • 「クラスごとに1つのファイルを書き込む」
  • 「データベース、コントローラー、ドメインからビューを分離する」。
  • スパゲッティの均質な単一のコードブロックを書くのではなく、一緒に動作する多くの個別のモジュールコンポーネントを書く

現在-この10年以内に-ほとんどのフレームワークで見られ、主張され、規約で主張され、コミュニティを介して伝えられているコードの方法とスタイルについてです。「モノリシックブロック」から「マイクロサービス」への考え方の転換です。それに伴い、マシンレベルのパフォーマンスとオーバーヘッド、さらにプログラマレベルのオーバーヘッドの面で価格が発生します。

元の質問は次のとおりです。

コンピュータサイエンスの分野では、プログラミングに関しては思考の著しい変化に気付きました。私はこのようなアドバイスを頻繁に目にします。

  • より小さな関数ごとのコードを記述します(この方法によりテストと保守が容易になります)
  • ほとんどのメソッド/関数の長さが数行になり、目的が明確になるまで、既存のコードをますます小さなコードチャンクにリファクタリングします(より大きなモノリシックブロックと比較して、より多くの関数を作成します)
  • 1つのことだけを行う関数を書く-関心の分離など(通常、スタック上により多くの関数とフレームを作成します)
  • より多くのファイルを作成します(ファイルごとに1つのクラス、MVC、ドメインアーキテクチャ、デザインパターン、オブジェクト指向などのレイヤーの目的のために、より多くのファイルシステム呼び出しを作成するために、分解の目的でより多くのクラス)

これは、2500行にまたがるメソッドと、すべてを行う大きなクラスと神オブジェクトを持つ「古い」または「時代遅れの」または「スパゲッティ」コーディングプラクティスと比較した変更です。

私の質問はこれです:

呼び出しがマシンコード、1と0、アセンブリ命令、HDDプラッターに至るとき、リファクタリングされたさまざまな小から小の関数とメソッドを備えた完全にクラス分離されたOOコードも生成されることを心配する必要がありますか余分なオーバーヘッドですか?

詳細

OOコードとそのメソッド呼び出しが最終的にASMでどのように処理されるか、またDB呼び出しとコンパイラー呼び出しがHDDプラッター上のアクチュエータアームの移動にどのように変換されるかについてはよく知りませんが、いくつかの考えがあります。追加の関数呼び出し、オブジェクト呼び出し、または(#include)呼び出し(一部の言語)は、追加の命令セットを生成するため、実際の「有用な」コードを追加することなく、コードの量を増やし、さまざまな「コードワイヤリング」オーバーヘッドを追加すると想定しています。また、ASMが実際にハードウェアで実行される前に、ASMに対して適切な最適化を実行できることも想像しますが、その最適化を実行できるのはそれだけです。

したがって、私の質問-十分に分離されたコード(何百ものファイル、クラス、デザインパターンなどに分割されるコード)が(スペースと速度で)どのくらいのオーバーヘッドをもたらすかは、「このオーバーヘッドのために、すべてを1つのモノリシックファイルに」

明確にするために更新:

同じコードを取得して分割し、リファクタリングし、より多くの関数とオブジェクト、メソッド、クラスに分離することで、より小さなコード間でより多くのパラメーターが渡されると想定しています。確かに、リファクタリングコードはスレッドを継続する必要があり、そのためにはパラメーターを渡す必要があります。より多くのメソッドまたはより多くのクラスまたはより多くのファクトリメソッドのデザインパターンは、単一のモノリシッククラスまたはメソッドの場合よりも、さまざまな情報を渡すオーバーヘッドを増やします。

どこか(TBDを引用)では、すべてのコードの最大70%がASMのMOV命令で構成され、実際の計算ではなくCPUレジスタに適切な変数をロードすると言われていました。私の場合、PUSH / POP命令を使用してCPUの時間を増やし、さまざまなコード間でリンケージとパラメーターの受け渡しを行います。コードを小さくするほど、より多くのオーバーヘッド「リンケージ」が必要になります。このリンケージがソフトウェアの肥大化とスローダウンに追加することを懸念しており、これを心配する必要があるのか​​、もしあれば、どれくらいの量を心配する必要があるのか​​、次の世紀のソフトウェアを構築しているプログラマーの現在と将来の世代、これらのプラクティスを使用して構築されたソフトウェアと共存し、使用する必要があります。

更新:複数のファイル

古いコードを徐々に置き換えている新しいコードを書いています。特に、古いクラスの1つが〜3000行のファイルであったことに注意しました(前述のとおり)。今では、テストファイルを含むさまざまなディレクトリに配置された15〜20のファイルのセットになりつつあり、いくつかのものを結合するために使用しているPHPフレームワークは含まれません。さらに多くのファイルが来ています。ディスクI / Oに関しては、複数のファイルのロードは、1つの大きなファイルのロードよりも遅くなります。もちろん、すべてのファイルがロードされるわけではなく、必要に応じてロードされ、ディスクキャッシングとメモリキャッシングのオプションが存在しますが、それでもメモリloading multiple filesよりも多くの処理が必要になると思いloading a single fileます。それを懸念に加えています。

更新:依存性のすべてを注入

しばらくしてこれに戻って..私の質問は誤解されたと思います。または、いくつかの答えを誤解することを選んだかもしれません。いくつかの答えが出てきたので、マイクロ最適化については話していません(少なくとも、マイクロ最適化について話していることは間違った呼び方だと思います)。 、コードのあらゆるレベルで。私は最近、このスタイルのコードがコンベンションの中心的なポイントの1つであり、Zend Conから来ました。ビューからロジック、モデルからビュー、データベースからモデルを分離し、可能であればデータベースからデータを分離します。依存関係-すべてを挿入します。これは、何もない配線コード(関数、クラス、ボイラープレート)を追加することを意味する場合があります、ただし、シーム/フックポイントとして機能し、ほとんどの場合、コードサイズを簡単に2倍にします。

更新2:「コードをより多くのファイルに分割する」ことは、パフォーマンスに大きな影響を与えますか(コンピューティングのすべてのレベルで)

compartmentalize your code into multiple files今日のコンピューティング(パフォーマンス、ディスク使用率、メモリ管理、CPU処理タスク)の哲学はどのように影響しますか?

私は話している

前...

それほど遠くない過去の非常に現実的な仮説では、モデルとビューおよびコントローラーのスパゲッティまたは非スパゲッティコーディングされたファイルの1つのモノブロックを簡単に記述できますが、既にロードされるとすべてを実行します。過去にCコードを使用していくつかのベンチマークを行ったところ、1つの900Mbファイルをメモリにロードして大きなチャンクで処理する方が、多数の小さなファイルをロードして小さなピースミールで処理するよりもはるかに高速であることがわかりました最終的に同じ作業を行うチャンク。

.. そしていま*

今日、私は元帳を表示するコードを見ていることに気付きました。これは、..アイテムが「注文」の場合、注文のHTMLブロックを表示するなどの機能を備えています。広告申込情報をコピーできる場合は、その背後にアイコンとHTMLパラメータを表示するHTMLブロックを印刷して、コピーを作成できるようにします。アイテムを上下に移動できる場合は、適切なHTML矢印を表示します。など、Zend Frameworkで作成できますpartial()これは、本質的には「パラメータを取得し、それも呼び出す別のHTMLファイルに挿入する関数を呼び出す」ことを意味します。取得する詳細度に応じて、元帳の最も小さな部分に対して個別のHTML関数を作成できます。1つは上向き矢印、下向き矢印、「このアイテムをコピーできます」などです。Webページの小さな部分を表示するためだけに複数のファイルを簡単に作成できます。私のコードと舞台裏のZend Frameworkコードを取り上げると、システム/スタックはおそらく20〜30近くの異なるファイルを呼び出します。

何?

私は、コードを多くの小さな個別のファイルに区分化することによって作成されるマシンの損耗などの側面に興味があります。

たとえば、より多くのファイルをロードするということは、ファイルシステムのさまざまな場所、および物理HDDのさまざまな場所にそれらを配置することを意味し、より多くのHDDシークおよび読み取り時間を意味します。

CPUの場合は、おそらくコンテキストの切り替えとさまざまなレジスタのロードが増えることを意味します。

このサブブロック(更新#2)では、複数のファイルを使用して、単一のファイルで実行できる同じタスクを実行し、システムのパフォーマンスにどのように影響するかに、より厳密に興味があります。

Zend Form APIと単純なHTMLの使用

Zend Form APIを最新かつ最高の最新OOプラクティスとともに使用して、検証付きのHTMLフォームを構築し、POSTドメインオブジェクトに変換しました。

35個のファイルを作成しました。

35 files = 
    = 10 fieldsets x {programmatic fieldset + fieldset manager + view template} 
    + a few supporting files

これらはすべて、いくつかの単純なHTML + PHP + JS + CSSファイル(おそらく合計4つの軽量ファイル)で置き換えることができます。

良いですか?それは価値がある?... 35個のファイルと、それらを機能させる多数のZend Zrameworkライブラリファイルをロードすることを想像してください。


1
素晴らしい質問。ベンチマークを行います(いくつかの良いケースを見つけるために1日ほどかかります)。ただし、この程度の速度の向上は、読みやすさと開発コストに多大なコストがかかります。私の最初の推測では、結果はごくわずかなパフォーマンスの向上です。
ダンサビン14

7
@Dan:1、5、10年のメンテナンス後にコードをベンチマークするためにカレンダーに入れてください。覚えている場合は、結果を確認します:)
mattnz 14

1
うんそれは本当のキッカーです。ルックアップを少なくし、関数呼び出しを高速化することに全員が同意すると思います。しかし、保守的かつ簡単に新しいチームメンバーをトレーニングするようなものよりも好まれた場合は想像できません。
ダンサビン14

2
明確にするために、プロジェクトに特定の速度要件があります。または、コードを「高速化」したいだけですか?後者の場合、私はそれについて心配しませんが、十分に高速であるが保守が容易なコードは、十分に高速であるが混乱しているコードよりもはるかに優れています。
コーマック

4
パフォーマンス上の理由で関数呼び出しを回避するという考えは、ダイクストラが時期尚早の最適化についての有名な引用で反発したまさにバカバカしい考えです。まじめな話、私はできません
RibaldEddie

回答:


10

私の質問はこれです:呼び出しがマシンコード、1と0、アセンブリ命令になったとき、さまざまな小規模から小規模の関数を持つクラスで区切られたコードが余分なオーバーヘッドを過度に生成することを心配する必要がありますか?

私の答えはイエスです、あなたはすべきです。あなたが小さな関数をたくさん持っているからではありません(かつて関数呼び出しのオーバーヘッドはかなり大きく、ループで100万回の小さな呼び出しを行うことでプログラムを遅くすることができましたが、今日のコンパイラはあなたのためにそれらをインラインし、残っているものが取られます) CPUファンシー予測アルゴリズムに注意してください。そのため心配する必要はありません)が、機能が小さすぎて頭を大きく理解できない場合は、プログラムにレイヤーを重ねすぎるという概念を導入するためです。大きなコンポーネントがある場合、同じ作業を何度も繰り返していないことを合理的に確信できますが、プログラムを非常に細かくして、実際に呼び出しパスを理解できない場合があり、その結果、何かが発生する可能性がありますそれはほとんど機能しません(そしてほとんどメンテナンスできません)。

たとえば、1つのメソッドを使用したWebサービスの参照プロジェクトを示した場所で働いていました。プロジェクトは32個の.csファイルで構成されていました-単一のWebサービス用です!これは非常に複雑すぎると考えました。各部分は小さく、それ自体で簡単に理解できますが、システム全体を説明することになると、私はすぐに、それが何をしているのかを見るために呼び出しをトレースする必要があることに気付きました(あなたが期待するように、あまりにも多くの抽象化が関係していました)。交換用のWebサービスは4つの.csファイルでした。

全体的にはほぼ同じであると考えたため、パフォーマンスを測定しませんでしたが、私の保守が大幅に安くなったことを保証できます。プログラマーの時間はCPU時間よりも重要であると誰もが話すとき、開発と保守の両方でプログラマーの時間を費やす複雑なモンスターを作成する場合、悪い振る舞いの言い訳をしているのではないかと考える必要があります。

どこか(TBDを引用)では、すべてのコードの最大70%がASMのMOV命令で構成され、実際の計算ではなくCPUレジスタに適切な変数をロードすると言われていました。

それはある、それらを加算または減算、CPUが、彼らはメモリからレジスタにビットを動かすんが何をした後、メモリに戻ってそれらを置きます。結局のところ、すべてのコンピューティングはほぼその結果になります。念のため、スレッドコードでの作業よりもコンテキスト切り替え(つまり、スレッドのレジスタ状態の保存と復元)のほとんどの時間を費やした非常にマルチスレッドのプログラムがありました。間違った場所での単純なロックは、そこのパフォーマンスを本当に台無しにしました。

だから私のアドバイスは次のとおりです。あなたのコードが他の人間によく見えるようにするために、どちらかの極端の間の賢明な中間点を見つけ、それがうまく機能するかどうかを確認するためにシステムをテストします。OS機能を使用して、CPU、メモリ、ディスク、およびネットワークIOで期待どおりに動作することを確認します。


私はこれが今私に最も話すと思います。難解なコードの分解と行き詰まり(つまり、必要なすべてのDIに縛られる)ではなく、特定の概念(DIなど)を使用しながら、マインドマッピングの概念からコードへのマッピングから始めることをお勧めします。それかどうか)。
デニス14年

1
個人的には、「 "モダン"」なコードのほうがプロファイリングがやや簡単であることがわかります。コードのメンテナンス性が高いほど、プロファイリングも簡単になると思います。保守性が低い...
AK_

25

細心の注意を払わないと、これらの懸念事項などのミクロな最適化は、維持できないコードにつながります。

最初は良いアイデアのように見えますが、プロファイラーはコードが高速であり、V&V / Test / QAが機能しているとさえ伝えます。すぐにバグが見つかり、要件の変更と考慮されなかった機能強化が求められます。

プロジェクトコードの寿命が短くなると、効率が低下します。保守可能なコードは、劣化が遅くなるため、保守不可能なコードよりも効率的になります。理由は、コードが変更されるとエントロピーが構築されるためです-

保守不能なコードは、すぐにデッドコード、冗長パス、および複製が増えます。これにより、より多くのバグが発生し、パフォーマンスを含むコードの劣化サイクルが発生します。やがて、開発者は自分が行っている変更が正しいという確信が低くなります。これにより、速度が低下し、慎重になり、一般に、見ることができる詳細のみに対処するため、さらにエントロピーが生じます。

小さなモジュールと単体テストを備えた保守可能なコードは変更が容易であり、不要になったコードは識別と削除が容易です。また、壊れたコードは識別しやすく、自信を持って修復または置換できます。

結局、ライフサイクル管理に帰着し、「これは高速なので、常に高速になります」ほど単純ではありません。

とりわけ、遅い正しいコードは、速い誤ったコードよりも無限に高速です。


ありがとう。これを私が行っていた場所に少し進めるために、私はマイクロ最適化について話しているのではなく、より小さなコードを記述し、より多くの依存性注入とより多くの外部機能部分を組み込み、より一般的なコードの「移動部分」に向かっていますそれらが機能するには、すべてを接続する必要があります。これにより、ハードウェアレベルで、より多くのリンケージ/コネクタ/変数の受け渡し/ MOV / PUSH / POP / CALL / JMPフラッフが生成されると思いがちです。また、コードの可読性への移行の価値も見ていますが、「fluff」のハードウェアレベルの計算サイクルを犠牲にします。
デニス14

10
パフォーマンス上の理由で関数呼び出しを避けることは絶対に最適化です!真剣に。マイクロ最適化のより良い例は考えられません。あなたが書いているソフトウェアの種類にとって実際にパフォーマンスの違いが重要であるという証拠はありますか?あなたは何も持っていないようです。
RibaldEddie 14年

17

私の理解では、インラインで指摘しているように、C ++のような低レベルのコードでは違いが生じる可能性がありますが、私は軽く言うことができます。

ウェブサイトはそれを要約しています- 簡単な答えはありません。システム、アプリケーションの動作、言語、コンパイラと最適化に依存します。

たとえば、C ++では、インラインでパフォーマンスを向上できます。多くの場合、それは何もしないか、パフォーマンスを低下させる可能性がありますが、物語を聞いたことがありますが、私は個人的にそれを経験していません。インラインは、コンパイラへの最適化の提案に過ぎず、無視できます。

より高いレベルのプログラムを開発している場合、最初からオーバーヘッドが存在する場合、オーバーヘッドは問題になりません。コンパイラは最近非常に賢く、とにかくこのようなものを処理する必要があります。多くのプログラマーは、次のことを実現するためのコードを持っています。コンパイラーを信頼しないでください。これが当てはまる場合、重要だと感じるわずかな最適化でも可能です。しかし、この点ですべての言語が異なることに留意してください。Javaは、実行時に自動的にインライン最適化を行います。Javascriptでは、Webページのインライン(個別のファイルとは対照的に)がブーストであり、Webページのミリ秒ごとにカウントされる場合がありますが、それはIOの問題です。

しかし、プログラマーがC ++のようなものと一緒に多くのマシンコード作業を行う可能性のある低レベルのプログラムでは、耳にしたことがすべての違いを生む可能性があります。ゲームは、特にコンソールでCPUパイプライン化が重要であり、インラインのようなものがあちこちに追加される場合の良い例です。

特にインラインでの良い読み物:http : //www.gotw.ca/gotw/033.htm


私の質問の観点から言えば、私はそれ自体インライン化に焦点を合わせていませんが、さまざまなコードをリンクするCPU、バス、およびI / Oの時間プロセスを占める「コード化された配線」に焦点を当てています。配線コードの50%以上と実際のコードの50%が実行したい点があるのではないかと思います。私が書くことができる最もタイトなコードでさえ多くの綿毛があり、それは人生の事実のようです。多くのビット上で実行され、レベルバイトが物流であることが実際のコードの- ..一箇所または別のジャンプ、ある場所から別の場所への値を動かし、そしてのみ時々行う追加
デニス・

...減算またはその他のビジネス関連機能。ループの展開が変数のインクリメントに割り当てられるオーバーヘッドが少ないためにいくつかのループを高速化できるように、ユースケースが設定されていれば、より大きな関数を作成するとおそらく速度が追加されます。ここでの私の懸念はより全体的で、小さなコードを書くための多くのアドバイスを見て、この配線を増やし、読みやすさを(できれば)向上させ、マイクロレベルでの肥大化を犠牲にします。
デニス14

3
@Dennis-考慮すべき1つのことは、OO言語では、プログラマが書くもの(a + b)と生成されるコード(2つのレジスタを単純に追加するか、メモリから移動するか?キャスティング、次に、オブジェクトのoperator +?への関数呼び出し。そのため、プログラマのレベルでの「小さな関数」は、マシンコードにレンダリングされた後は小さなものではないかもしれません。
マイケルコーネ14

2
@Dennis Windows用のASMコード(直接、コンパイルされていない)の記述は、「mov、mov、invoke、mov、mov、invoke」の行に沿っていると言えます。invokeはpush / popsでラップされた呼び出しを行うマクロであるため...独自のコードで関数呼び出しを行うこともありますが、すべてのOS呼び出しによってd小化されます。
ブライアンノブラウチ14年

12

これは、2500行にまたがるメソッドがあり、大きなクラスがすべてを実行する「古い」または「悪い」コードプラクティスと比較した変更です。

これを行うことは良い習慣だと思った人はいないと思います。そして、パフォーマンス上の理由で、それをやった人々がそれをやったとは思えません。

ドナルド・クヌースの有名な引用はここに非常に関連があると思います:

効率が小さいことを忘れてはなりません。時間の約97%は言うべきです。時期尚早の最適化はすべての悪の根源です。

したがって、コードの97%で、適切なプラクティスを使用し、小さなメソッドを作成するだけです(意見の問題であるため、すべてのメソッドが数行であるとは思わない)。残りの3%は、パフォーマンス重要です、それを測定します。また、多くの小さなメソッドを使用すると実際にコードの速度が大幅に低下することが測定で示されている場合は、それらを組み合わせて大きなメソッドにする必要があります。しかし、メンテナンス容易でないコードを書くのは、それ速いかもしれないという理由だけで書かないでください。


11

経験豊富なプログラマーや現在の考え方に耳を傾ける必要があります。長年にわたって大規模なソフトウェアを扱ってきた人々は、何か貢献することができます。

私の経験では、これが減速の原因であり、小さくはありません。それらは桁違いです:

  • コードの行が他の行とほぼ同じくらいの時間がかかるという仮定。たとえば、cout << endla = b + c。前者は後者の数千倍の時間がかかります。Stackexchangeには、「このコードを最適化するさまざまな方法を試しましたが、違いはないようです」という形式の質問がたくさんあります。途中で古い関数呼び出しがある場合。

  • 関数やメソッドの呼び出しは、一度記述されると当然必要です。関数とメソッドは簡単に呼び出すことができ、呼び出しは通常かなり効率的です。問題は、クレジットカードのようなものだということです。彼らはあなたが本当に望んでいるよりも多くを使うように誘惑し、あなたが使ったものを隠す傾向があります。それに加えて、大規模なソフトウェアは抽象化のレイヤーの上にレイヤーを持っているので、各レイヤーで15%の無駄しかなくても、5レイヤー以上がスローダウンファクター2になります。これに対する答えは、機能を削除したり、より大きな機能、それはこの問題を警戒し、喜んで根ざすことができるように自分を鍛えることです。

  • ギャロッピングの一般性。抽象化の価値は、より少ないコードでより多くのことができるということです-少なくともそれは希望です。このアイデアは極端にプッシュすることができます。一般性が多すぎるという問題は、すべての問題が固有であり、一般的な抽象化でそれを解決する場合、それらの抽象化は必ずしも問題の特定の特性を活用できないことです。たとえば、長さが3を超えない場合に、大きなサイズで効率的である可能性のある派手な優先度のキュークラスが使用される状況を見ました。

  • ギャロッピングデータ構造。OOPは非常に便利なパラダイムですが、データ構造を最小限に抑えることは推奨しません。むしろ、その複雑さを隠そうとすることを推奨します。たとえば、データAが何らかの方法で変更された場合、Aは通知イベントを発行し、BとCがアンサンブル全体の一貫性を保つように自身を変更できるようにする「通知」の概念があります。これは多くのレイヤーに広がり、修正のコストを非常に大きくする可能性があります。その後、Aへの変更がすぐに元に戻される可能性があります。または、さらに別の変更に変更されました。つまり、アンサンブルの一貫性を保つために費やされた努力は、再び行われなければなりません。混乱は、これらすべての通知ハンドラーのバグの可能性、および循環性などです。データ構造を正規化して、変更を1か所でのみ行う必要があるようにすることをお勧めします。正規化されていないデータを回避できない場合は、短いリーシュで一貫性を保つように見せかけるのではなく、定期的なパスで矛盾を修復することをお勧めします。

...さらに考えてみると、追加します。


8

短い答えは「はい」です。そして、一般的に、コードは少し遅くなります。

ただし、適切なオブジェクト指向のリファクタリングにより、コードを高速化する最適化が明らかになる場合があります。複雑なネストされたオブジェクトの配列ではなく、適切なデータ構造、ゲッターなどを使用して、複雑なJavaアルゴリズムをよりオブジェクト指向にした1つのプロジェクトに取り組みました。しかし、データ構造へのアクセスをより適切に分離および制限することで、Doubleの巨大な配列(空の結果にはnullを使用)から、組織化されたdoubleの配列に変更でき、空の結果にはNaNを使用できました。これにより、速度が10倍向上しました。

補遺:一般的に、より小さく、より構造化されたコードは、マルチスレッドに対応しやすく、大幅な高速化を実現する最良の方法です。


1
Doubles からsに切り替えるにはdouble、より良い構造化コードが必要になる理由がわかりません。
svick 14年

うわー、下票?元のクライアントコードはDouble.NaNを処理しませんでしたが、空の値を表すためにnullをチェックしていました。再構築後、さまざまなアルゴリズム結果のゲッターを使用して、これを(カプセル化を介して)処理できます。確かに、クライアントコードを書き直すこともできましたが、これは簡単でした。
user949300 14年

記録のために、私はこの答えを否定した人ではありませんでした。
svick 14年

7

とりわけ、プログラミングはトレードオフに関するものです。この事実に基づいて、私は「はい」と答える傾向があります。しかし、見返りに何を得るかを考えてください。読み取り可能、再利用可能、および簡単に変更可能なコードを取得することは、考えられる欠点よりも簡単に重くなります。

@ user949300が言及したように、そのようなアプローチでアルゴリズム的またはアーキテクチャ的に改善できる領域を見つけるのは簡単です。これらは通常、はるかに有益かつ効果的な持っていないよりも改善する可能 OOまたは関数呼び出しのオーバーヘッド(すでに単なるノイズであるIベット)を。


また、ASMが実際にハードウェアで実行される前に、ASMに対して適切な最適化を行うことができると思います。

このようなことが私の頭に浮かぶときはいつでも、コンパイラーに取り組んでいる最も賢い人々が費やした数十年は、おそらくマシンコードの生成においてGCCのようなツールをはるかに優れたものにしていることを覚えています。何らかのマイクロコントローラー関連のものに取り組んでいない限り、心配しないでください。

コードにますます多くの関数とますます多くのオブジェクトとクラスを追加すると、より小さなコード部分の間でより多くのパラメータが渡されると仮定しています。

最適化の際に何かが時間の無駄であると仮定すると、コードのパフォーマンスに関する事実が必要になります。プログラムがほとんどの時間を専用のツールで費やしている場所を見つけ、それを最適化し、繰り返します。


まとめると、コンパイラに任せて、アルゴリズムやデータ構造の改善などの重要なことに集中してください。あなたがあなたの質問で言及したすべてのパターンは、あなたを支援するために存在し、それらを使用します。

PS:これら2つのCrockfordの講演は私の頭に浮かびました。1つ目は、非常に簡潔なCS履歴です(正確な科学については常に知っていると良いでしょう)。2つ目は、なぜ良いものを拒否するのかということです。


この答えが一番好きです。人間はコンパイラを二度と推測し、ボトルネックのある場所でショットを撮るのは恐ろしいです。もちろん、大きなO時間の複雑さは知っておくべきことですが、大きな100行のスパゲッティメソッドとファクトリメソッドの呼び出し+いくつかの仮想メソッドのディスパッチは議論になりません。その場合、パフォーマンスはまったく面白くありません。また、確固たる事実や測定なしで「最適化」することは時間の無駄であることに注意することは大きなプラスです。
サラ

4

あなたが特定した傾向は、ソフトウェアの開発に関する真実を指し示していると思います-プログラマーの時間はCPUの時間よりも高価です。これまでのところ、コンピューターの高速化と低価格化は進んでいますが、複雑なアプリケーションの混乱は、変更するのに数千時間ではなくても数百時間かかります。給与、福利厚生、オフィススペースなどのコストを考えると、実行速度が少し遅いが、変更がより速くて安全なコードを作成する方が費用対効果が高くなります。


1
私は同意しますが、モバイルデバイスは非常に人気が高まっているため、大きな例外だと思います。処理能力は向上していますが、iPhoneアプリを作成することはできず、Webサーバーでできるようにメモリを追加できると期待しています。
ジェフ14

3
@JeffO:私は同意しません-クアッドコアプロセッサを搭載したモバイルデバイスは現在、通常のパフォーマンスです(特にバッテリ寿命に影響するため)が、懸念は安定性ほど重要ではありません。遅い携帯電話やタブレットは悪い評価を受け、不安定なものは虐殺されます。モバイルアプリは非常に動的であり、ほぼ毎日変化します。ソースは維持する必要があります。
mattnz

1
@JeffO:Androidで使用されている仮想マシンは、価値があるものとしてはかなり悪いものです。ベンチマークによると、ネイティブコードよりも桁違いに遅くなる可能性があります(通常、最善の組み合わせは通常少し遅くなります)。誰も気にしません アプリケーションの作成は高速であり、CPUは親指をいじりながら90%の時間ユーザー入力を待機します。
Jan Hudec 14年

パフォーマンスには、生のCPUベンチマーク以上のものがあります。私のAndroidスマートフォンは、AVが更新をスキャンしていて、思ったより長くハングしているように見える場合と、クアッドコア2Gb RAMモデルを除いて、正常に動作します!今日では、帯域幅(ネットワークまたはメモリ)がおそらく主なボトルネックです。超高速CPUは、おそらく99%の時間を親指でいじっていますが、全体的なエクスペリエンスはまだ不十分です。
gbjbaanb 14年

4

さて、20年以上前、新しいものではなく「古いものでも悪いものでもない」とお考えの方は、印刷されたページに収まるように関数を十分に小さくすることがルールでした。ドットマトリックスプリンターがあったので、行数は多少固定されていました。一般に、1ページあたりの行数の選択は1つまたは2つだけです。

問題、保守性、パフォーマンス、テスト容易性、読みやすさの多くの側面を尋ねています。パフォーマンスに傾くほど、コードの保守性と可読性が低下するため、個々のプログラマーごとに異なる可能性のある快適性レベルを見つける必要があります。

コンパイラによって生成されたコード(マシンコードを使用する場合)に関しては、関数が大きいほど、レジスタの中間値をスタックにスピルする必要があります。スタックフレームが使用される場合、スタックの消費はより大きなチャンクになります。関数が小さいほど、データがレジスタに留まる可能性が高くなり、スタックへの依存度が低くなります。自然に機能ごとに必要なスタックの小さなチャンク。スタックフレームには、パフォーマンスに関して長所と短所があります。より小さな関数は、より多くの関数のセットアップとクリーンアップを意味します。もちろん、どのようにコンパイルするか、コンパイラにどのような機会を与えるかにも依存します。2500行関数の1つではなく、10行関数を250個持つことができます。コンパイラーは、全体を最適化することができる場合にコンパイラが取得する2500行関数です。しかし、これらの250の10行関数を使用して、それらを2、3、4、250の個別のファイルに分散し、各ファイルを個別にコンパイルすると、コンパイラは、可能な限り多くのデッドコードを最適化できなくなります。ここで一番下の行は、両方に賛否両論があり、これに一般的なルールを置くことは不可能であるか、それが最良の方法です。

合理的なサイズの関数、つまり画面またはページで(合理的なフォントで)見ることができるものは、非常に大きなコードよりも消費しやすく理解できるものです。しかし、他の多くの小さな関数を呼び出す他の多くの小さな関数を呼び出す小さな関数である場合、そのコードを理解するにはいくつかのウィンドウまたはブラウザが必要なので、読みやすさの面では何も購入しません。

UNIXの方法は、私の用語である、洗練されたレゴブロックを使用することです。テープの使用を止めてから何年も経ってテープ機能を使用するのはなぜですか?BLOBは非常にうまく機能し、裏側ではテープインターフェイスをファイルインターフェイスに置き換えて、プログラムの中身を利用することができます。ideに取って代わる支配的なインターフェイスとしてscsiがなくなったという理由だけでcdrom書き込みソフトウェアを書き換える理由(後で戻ってきます)。ここでも、洗練されたサブブロックを活用し、一方の端を新しいインターフェイスブロックに置き換えます(ハードウェア設計者が単にハードウェアデザインにインターフェイスブロックを追加して、場合によってはscsiドライブにscsiインターフェイスを作成することも理解してください。 、明確に定義された目的と明確に定義された入力および出力を備えた、適切なサイズの洗練されたレゴブロックを構築します。これらのレゴブロックにテストをラップし、同じブロックを取り、同じブロックとブロックにユーザーインターフェイスとオペレーティングシステムインターフェイスをラップすることができます。理論的には十分にテストされ、よく理解されているので、追加の新しいブロックを追加するだけです。両端に。すべてのブロックインターフェイスが適切に設計され、機能が十分に理解されている限り、最小限の接着剤で非常に多くのものを構築できます。既知のサイズと形状の青と赤、黒と黄色のレゴブロックと同様に、非常に多くのものを作成できます。すべてのブロックインターフェイスが適切に設計され、機能が十分に理解されている限り、最小限の接着剤で非常に多くのものを構築できます。既知のサイズと形状の青と赤、黒と黄色のレゴブロックと同様に、非常に多くのものを作成できます。すべてのブロックインターフェイスが適切に設計され、機能が十分に理解されている限り、最小限の接着剤で非常に多くのものを構築できます。既知のサイズと形状の青と赤、黒と黄色のレゴブロックと同じように、非常に多くのものを作成できます。

個人はそれぞれ異なり、洗練され、明確に定義され、テストされ、読みやすいという定義は異なります。たとえば、教授がプログラミングルールを決定するのは理不尽ではありません。それは、プロとしてのあなたにとって悪いこともそうでないこともありますが、場合によっては、教授や大学院生のアシスタントでコードを読んでグレーディングする作業を簡単にすることです...専門的には、職業ごとにさまざまな理由で異なるルールがある可能性があります。通常、権力のある1人または数人の人が何かについて意見を持っていますが、正しいか間違っているか、その力であなたはそうすることができますそこにある(またはやめる、解雇される、何らかの形で権力を握る)。これらのルールは、読みやすさ、パフォーマンス、テスト容易性、移植性に関する何らかの事実に基づいているのと同じくらいしばしば意見に基づいています。


「したがって、個々のプログラマーごとに異なる可能性のある快適レベルを見つける必要があります」最適化のレベルは、各プログラマーの好みではなく、各コードのパフォーマンス要件によって決定されるべきだと思います。
svick 14年

コンパイラーは、プログラマーがコンパイラーを最適化するようにコンパイラーに指示したと仮定すると、コンパイラーはデフォルトで最適化しないようにデフォルト設定します(コマンドラインでは、IDEが使用される場合、異なるデフォルトがあります)。しかし、プログラマーは、関数のサイズを最適化するのに十分な知識がないかもしれません。手動チューニングのパフォーマンスは、可読性、特に保守性に悪影響を与える傾向がありますが、テスト容易性はどちらの方向にも進む可能性があります。
old_timer

2

コンパイラがどれだけ賢いかに依存します。一般的に、オプティマイザーを裏切ることは悪い考えであり、実際にコンパイラーから最適化の機会を奪う可能性があります。手始めに、おそらくあなたはそれが何ができるかについて全く手掛かりを持っていないし、あなたが実際に行うことのほとんどはそれがどれだけうまくいくかに影響する。

時期尚早の最適化とは、プログラマーがそれを行おうとして、やろうとしていることのクリティカルパスに実際にはないコードを維持するのが難しいという概念です。アプリがIOイベントを待って実際にほとんどの時間ブロックされているときに、できるだけ多くのCPUを絞り出そうとすることは、たとえば私がよく目にすることです。

最善の方法は、正確性のためにコーディングし、プロファイラーを使用して実際のパフォーマンスのボトルネックを見つけ、クリティカルパスにあるものと改善できるかどうかを分析してそれらを修正することです。多くの場合、いくつかの簡単な修正が大いに役立ちます。


0

[computer-time element]

Looserカップリングとより小さな関数へのリファクタリングはコードの速度に影響しますか?

はい、そうです。しかし、この「シーム/配線」コードを削除するのは、インタープリター、コンパイラー、およびJITコンパイラー次第であり、他のコードよりも優れているものもありますが、そうでないものもあります。

複数ファイルの懸念はI / Oオーバーヘッドに追加されるため、(コンピューター時間で)速度にもかなり影響します。

[人間速度の要素]

(そして気にする必要がありますか?)

いいえ、気にする必要はありません。最近、コンピューターと回路は非常に高速であり、ネットワーク遅延、データベースI / O、キャッシュなどの他の要因が引き継いでいます。

そのため、ネイティブコードの実行自体の2倍から4倍の速度低下は、これらの他の要因によってしばしばおぼれます。

複数ファイルの読み込みに関しては、多くの場合、さまざまなキャッシングソリューションによって処理されます。物事を最初にロードしてマージするのに時間がかかる場合がありますが、毎回静的ファイルの場合、キャッシュは単一のファイルがロードされているかのように機能します。キャッシングは、複数ファイルのロードに対するソリューションとして提供されます。


0

答え(見逃した場合)

はい、気にする必要がありますが、パフォーマンスではなくコードの書き方に注意してください。

要するに

パフォーマンスを気にしない

質問の文脈では、よりスマートなコンパイラーとインタープリターがすでにそれを処理しています

保守可能なコードの作成に注意する

保守コストが合理的な人間の理解のレベルにあるコード。すなわち、それぞれを理解してもコードを理解不能にする1000個の小さな関数を作成しないでください。また、理解するには大きすぎる1つのgodオブジェクト関数を作成しないでください。メンテナンスが簡単。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.