私たちは皆、早すぎる最適化が読みにくい/維持できないコードにつながるため、すべての悪の根源であることを知っています。さらに悪いのは、誰かが「最適化」を実装するときのペシミゼーションです。思考速くなりますが、遅くなり、バグがあり、保守不能になるなどです。最もばかげた例は何ですかあなたが見たこれの?
古いプロジェクトでは、Z-8000の大規模な経験を持つ組み込みシステムプログラマー(そうでなければ優れた)を引き継ぎました。
新しい環境は32ビットSparc Solarisでした。
RAMから16ビットを取得するほうが32ビットを取得するよりも高速だったため、コードを高速化するために、すべての整数をショートに変更しました。
32ビットシステムで32ビット値を取得する方が16ビット値を取得するよりも高速であることを示し、16ビット値を取得するにはCPUが32ビット幅にする必要があることを説明するデモプログラムを作成する必要がありましたメモリアクセスしてから、16ビット値に不要なビットをマスクアウトまたはシフトします。
「時期尚早な最適化はすべての悪の根源である」というフレーズは、ずっと使われていると思います。多くのプロジェクトでは、プロジェクトの後半までパフォーマンスを考慮しないという言い訳になっています。
多くの場合、このフレーズは人々が仕事を避けるための松葉杖です。このフレーズは、人々が本当に「うん、前もってそれを考えていなかったので、今対処する時間がない」と言うべきときに使われます。
「悲観的」に起因して導入された問題の例よりも、パフォーマンスの問題が馬鹿げているという「ばかげた」例を見たことがあります。
私がより良いと思うのはこれです:「測定と理解なしでの最適化はまったく最適化ではありません-その単なるランダムな変更」。
優れたパフォーマンスの作業には時間がかかります。多くの場合、機能またはコンポーネント自体の開発に時間がかかります。
データベースは悲観的な遊び場です。
お気に入りが含まれます:
それは私の頭上です。
絶対的なルールはないと思います。前もって最適化されているものとそうでないものがあります。
たとえば、私は衛星からデータパケットを受信する会社で働いていました。各パケットには多大な費用がかかるため、すべてのデータは高度に最適化されています(つまり、パックされています)。たとえば、緯度/経度は絶対値(浮動小数点数)としてではなく、「現在の」ゾーンの「北西」コーナーに対するオフセットとして送信されました。使用する前にすべてのデータを解凍する必要がありました。しかし、これは悲観的ではなく、通信コストを削減するためのインテリジェントな最適化だと思います。
一方、ソフトウェアアーキテクトは、展開されたデータを非常に読みやすいXMLドキュメントにフォーマットし、データベースに保存することを決定しました(各フィールドを対応する列に保存するのではなく)。彼らの考えは、「XMLは未来」、「ディスクスペースは安価」、「プロセッサは安価」であり、何も最適化する必要がないというものでした。その結果、16バイトのパケットが1つの列に格納された2kBのドキュメントに変換され、単純なクエリでも、メガバイトのXMLドキュメントをメモリに読み込む必要がありました。毎秒50パケット以上を受信したので、パフォーマンスがどれほどひどいものになったか想像できます(BTW、会社は破産しました)。
繰り返しますが、絶対的なルールはありません。はい、最適化が早すぎることは間違いです。しかし、時々「CPU /ディスク容量/メモリは安い」というモットーがすべての悪の本当の根源です。
ああ主よ、私はそれらすべてを見たと思います。たいていの場合、パフォーマンスの問題の原因に至るまでトラブルシューティングを行うのが面倒で、実際にISこれらのケースの多くでは、特定の技術を試したいと思っている人が、光沢のある新しいハンマーに合うネイルを必死に探しているだけのケースではないかと思います。
ここに最近の例があります:
データアーキテクトは、かなり大規模で複雑なアプリケーションでキーテーブルを垂直方向にパーティション分割するという精巧な提案をしてきました。彼は、変化に対応するためにどのような種類の開発努力が必要かを知りたいと思っています。会話は次のようになりました。
Me:なぜこれを検討しているのですか?解決しようとしている問題は何ですか?
Him:テーブルXは幅が広すぎるため、パフォーマンス上の理由からパーティション化しています。
Me:何が幅が広すぎると思いますか?
Him:コンサルタントは、1つのテーブルに含めるには列が多すぎると言いました。
Me:そして、これはパフォーマンスに影響を与えていますか?
Him:はい、ユーザーはアプリケーションのXYZモジュールの断続的なスローダウンを報告しています。
Me:テーブルの幅が問題の原因であることをどのように確認しますか?
Him:これはXYZモジュールで使用されるキーテーブルであり、200列のようなものです。それは問題に違いない。
Me(説明):しかし、特にモジュールXYZはそのテーブルのほとんどの列を使用し、ユーザーがそのテーブルから表示したいデータを表示するようにアプリを構成するため、使用する列は予測不能です。とにかくすべてのテーブルを結合して戻す時間の95%は、hurtパフォーマンスになる可能性があります。
彼:コンサルタントは、それが広すぎるので変更する必要があると言った。
Me:このコンサルタントは誰ですか?私たちがコンサルタントを雇ったことも知らなかったし、彼らが開発チームと話をしたこともまったくなかった。
彼:まあ、まだ彼らを雇っていない。これは彼らが提供した提案の一部ですが、彼らはこのデータベースを再構築する必要があると主張しました。
私:うーんしたがって、データベースの再設計サービスを販売するコンサルタントは、データベースの再設計が必要だと考えています。
会話はこのように続いた。その後、問題のテーブルをもう一度見て、エキゾチックなパーティション戦略を必要とせずに、単純な正規化でテーブルを狭めることができると判断しました。もちろん、これはパフォーマンスの問題(以前は報告されていませんでした)を調査し、それらを2つの要因まで追跡すると、重要なポイントであることが判明しました。
もちろん、建築家はまだ「広すぎる」メタ問題にぶら下がっているテーブルの垂直分割を推進しています。彼は、アプリを見たり、パフォーマンス分析を実行したりせずに、データベースの主要な設計変更が必要であると判断できる別のデータベースコンサルタントから提案を得ることで、ケースを強化しました。
Alphadrive-7を使用してCHX-LTを完全に培養する人々を見てきました。これは一般的ではありません。より一般的な方法は、ZTトランスフォーマーを初期化してバッファリングを減らし(ネットのオーバーロード抵抗が大きくなるため)、Javaスタイルバイトグラフィックスを作成します。
完全に悲観的です!
私は認めていますが、Javaのループ外でStringBufferを使用して文字列を連結する人々を捕まえました。回転のようなシンプルなものでした
String msg = "Count = " + count + " of " + total + ".";
に
StringBuffer sb = new StringBuffer("Count = ");
sb.append(count);
sb.append(" of ");
sb.append(total);
sb.append(".");
String msg = sb.toString();
以前は、ループ内でこの手法を使用するのがかなり一般的でしたが、これはかなり高速だったためです。問題は、StringBufferは同期されているため、少数のストリングを連結するだけの場合、実際には余分なオーバーヘッドが発生することです。 (言うまでもなく、このスケールでは違いは絶対に些細なものです。)このプラクティスに関する他の2つのポイント:
「ルート」テーブルを使用するMSSQLデータベースを見たことがあります。 Rootテーブルには4つの列がありました:GUID(一意の識別子)、ID(int)、LastModDate(datetime)、およびCreateDate(datetime)。データベース内のすべてのテーブルはルートに外部キーされました。テーブル。dbのanyテーブルに新しい行が作成されるたびに、実際のテーブルにアクセスする前に、いくつかのストアドプロシージャを使用してルートテーブルにエントリを挿入する必要がありました。 (いくつかのトリガーを使用してデータベースがジョブを実行するのではなく、単純なトリガー)。
これは無用な耳障りな頭痛と頭痛の種を作成し、sprocsを使用するためにその上に書かれたものを必要としました(そして、LINQを会社に導入するという私の希望を排除しました。それがすることになっていたことさえ達成しません。
このパスを選択した開発者は、テーブル自体でGUIDを使用していなかったため、これにより大量のスペースが節約されたという前提でそれを擁護しました(ただし、... GUID作成するすべての行のルートテーブル?)、何らかの方法でパフォーマンスを改善し、データベースへの変更を監査するのを「簡単」にしました。
ああ、そして、データベース図は地獄からの突然変異クモのように見えました。
POBI-意図による明らかな悲観?
90年代の私の同僚は、CEOがすべてのERPソフトウェア(カスタム1)リリースの最初の日を費やしてパフォーマンスの問題を突き止めたからです新しい機能がギガバイトを処理し、不可能を可能にしたとしても、彼は常にいくつかの詳細、または一見大きな問題であることに気づきました。彼はプログラミングについて多くのことを知っていると信じ、プログラマーのケツを蹴りました。
批判は無能であるため(彼はIT担当者ではなくCEOでした)、私の同僚はそれを正しく理解することができませんでした。パフォーマンスの問題がない場合、それを排除することはできません...
1つのリリースまで、彼は新しいコードに多くのDelay(200)関数呼び出し(Delphiでした)を追加しました。本番開始後わずか20分で、CEOのオフィスに出頭し、期限切れのin辱を直視するよう命じられました。
彼が戻って、笑って、冗談を言って、BigMacに出かけたときに同僚がミュートになったのは今まで珍しいことだけでした。 。
当然、私の同僚は彼のデスクで1、2日間休息し、Quakeでの照準スキルを向上させました。2日目または3日目に、Delayコールを削除し、再構築して、Wordを広めた「緊急パッチ」をリリースしましたパフォーマンスホールを修正するために1泊2日費やしていたこと。
これは、悪のCEOが「素晴らしい仕事だ!」彼に。重要なのはそれだけですよね?
これは本当のPOBIでした。
しかし、これは一種のソーシャルプロセスの最適化でもあるため、100%大丈夫です。
おもう。
「データベースの独立性」。これは、ストアドプロシージャやトリガーなどがなく、外部キーもないことを意味していました。
var stringBuilder = new StringBuilder();
stringBuilder.Append(myObj.a + myObj.b + myObj.c + myObj.d);
string cat = stringBuilder.ToString();
私が今まで見たStringBuilderの最適な使用法。
単純なstring.splitで十分な場合に正規表現を使用して文字列を分割する
私が知っているこのスレッドには非常に遅れていますが、最近これを見ました:
bool isFinished = GetIsFinished();
switch (isFinished)
{
case true:
DoFinish();
break;
case false:
DoNextStep();
break;
default:
DoNextStep();
}
ブール値に余分な値があった場合に備えて...
私が考えることができる最悪の例は、すべての従業員に関する情報を含む私の会社の内部データベースです。 HRから毎晩更新され、ASP.NET Webサービスが最上位にあります。他の多くのアプリは、Webサービスを使用して、検索/ドロップダウンフィールドなどを設定します。
悲観論は、開発者が、Webサービスへの繰り返しの呼び出しは、SQLクエリの繰り返しを行うには遅すぎると考えたということです。それで彼は何をしましたか?アプリケーション開始イベントはデータベース全体を読み取り、すべてをメモリ内のオブジェクトに変換し、アプリプールがリサイクルされるまで無期限に保存されます。このコードは非常に遅いため、2000人未満の従業員でロードするのに15分かかります。日中にアプリプールを誤ってリサイクルした場合、各Webサービスリクエストが複数の同時リロードを開始するため、30分以上かかる可能性があります。このため、アカウントが作成された最初の日は新規採用者がデータベースに表示されないため、最初の数日間はほとんどの内部アプリにアクセスできず、親指をいじっています。
悲観論の2番目のレベルは、開発マネージャーが依存アプリケーションの破損を恐れてそれに触れたくないということですが、そのような単純なコンポーネントの設計が不十分なために、重要なアプリケーションが散発的に全社的に停止し続けています。
ソートについて誰も言及していないようです。
何度か、私は誰かがバブルソートを手作りしたことを発見しました。なぜなら、状況はすでに存在する「あまりにも派手な」クイックソートアルゴリズムへの呼び出しを「必要としなかった」からです。開発者は、テスト用に使用している10行のデータで、手作りのバブルソートが十分に機能したことで満足しました。顧客が数千行を追加した後も、それほどうまくいきませんでした。
これらのGemをConstantsクラスに含めたコードを変更しようとしたことがありました
public static String COMMA_DELIMINATOR=",";
public static String COMMA_SPACE_DELIMINATOR=", ";
public static String COLIN_DELIMINATOR=":";
これらはそれぞれ、アプリケーションの残りの部分で異なる目的で複数回使用されました。 COMMA_DELIMINATORは、8つの異なるパッケージで200を超える用途でコードを散らかしました。
社内ソフトウェアで何度も何度も遭遇する、史上最大のナンバーワン:
「移植性」の理由でDBMSの機能を使用しないのは、「後で別のベンダーに切り替えたい可能性があるため」
私の唇を読んで。社内作業の場合:それは起こりません!
私はかつて、次のようなコードで満たされたアプリを開発しました。
1 Tuple *FindTuple( DataSet *set, int target ) {
2 Tuple *found = null;
3 Tuple *curr = GetFirstTupleOfSet(set);
4 while (curr) {
5 if (curr->id == target)
6 found = curr;
7 curr = GetNextTuple(curr);
8 }
9 return found;
10 }
単にfound
を削除し、最後にnull
を返し、6行目を次のように変更します。
return curr;
アプリのパフォーマンスが2倍になりました。
私は、Cコンパイラのオプティマイザーとルーチンの書き換えコードを、彼だけが読めるようにしようとしていた同僚がいました。彼の好きなトリックの1つは、次のような読み取り可能なメソッドを変更することでした(コードを作成する)。
int some_method(int input1, int input2) {
int x;
if (input1 == -1) {
return 0;
}
if (input1 == input2) {
return input1;
}
... a long expression here ...
return x;
}
これに:
int some_method() {
return (input == -1) ? 0 : (input1 == input2) ? input 1 :
... a long expression ...
... a long expression ...
... a long expression ...
}
つまり、一度読み取れるメソッドの最初の行は「return
」になり、他のすべてのロジックは深くネストされた3次式に置き換えられます。これがどのように維持できないかについて議論しようとしたとき、彼は彼の方法のアセンブリ出力が3つまたは4つのアセンブリ命令よりも短いという事実を指摘するでしょう。必ずしもfasterとは限りませんでしたが、常にtiny少し短くなりました。これは、メモリ使用量がときどき問題になる組み込みシステムでしたが、コードを読みやすくするよりもはるかに簡単な最適化を行うことができました。
その後、何らかの理由でptr->structElement
が読みにくいと判断したため、読みやすく高速であるという理論に基づいて、これらすべてを(*ptr).structElement
に変更し始めました。
読み取り可能なコードを読み取り可能なコードに変換すると、せいぜい1%の改善になり、実際にはコードが遅くなることがあります。
本格的な開発者としての最初の仕事の1つで、スケーリングの問題を抱えているプログラムのプロジェクトを引き継ぎました。小さいデータセットでは十分に機能しますが、大量のデータを指定すると完全にクラッシュします。
掘り下げてみると、元のプログラマーは、分析を並列化することで物事を高速化しようとしていることがわかりました-追加のデータソースごとに新しいスレッドを起動します。しかし、彼はすべてのスレッドがデッドロックしている共有リソースを必要とするという間違いを犯していました。もちろん、並行性の利点はすべてなくなりました。さらに、ほとんどのシステムがクラッシュして、100以上のスレッドを起動して、そのうちの1つを除くすべてをロックしました。私の強力な開発マシンは、約6時間で150のソースデータセットをかき回したという点で例外でした。
それを修正するために、マルチスレッドコンポーネントを削除し、I/Oをクリーンアップしました。他の変更がなければ、150のソースデータセットの実行時間は私のマシンでは10分未満に、平均的な企業のマシンでは無限から30分未満に低下しました。
私はこの宝石を提供できると思います:
unsigned long isqrt(unsigned long value)
{
unsigned long tmp = 1, root = 0;
#define ISQRT_INNER(shift) \
{ \
if (value >= (tmp = ((root << 1) + (1 << (shift))) << (shift))) \
{ \
root += 1 << shift; \
value -= tmp; \
} \
}
// Find out how many bytes our value uses
// so we don't do any uneeded work.
if (value & 0xffff0000)
{
if ((value & 0xff000000) == 0)
tmp = 3;
else
tmp = 4;
}
else if (value & 0x0000ff00)
tmp = 2;
switch (tmp)
{
case 4:
ISQRT_INNER(15);
ISQRT_INNER(14);
ISQRT_INNER(13);
ISQRT_INNER(12);
case 3:
ISQRT_INNER(11);
ISQRT_INNER(10);
ISQRT_INNER( 9);
ISQRT_INNER( 8);
case 2:
ISQRT_INNER( 7);
ISQRT_INNER( 6);
ISQRT_INNER( 5);
ISQRT_INNER( 4);
case 1:
ISQRT_INNER( 3);
ISQRT_INNER( 2);
ISQRT_INNER( 1);
ISQRT_INNER( 0);
}
#undef ISQRT_INNER
return root;
}
平方根は非常にデリケートな場所で計算されたため、それを高速化する方法を検討するタスクを得ました。この小さなリファクタリングにより、実行時間が3分の1短縮されました(使用されるハードウェアとコンパイラの組み合わせ、YMMVの場合)。
unsigned long isqrt(unsigned long value)
{
unsigned long tmp = 1, root = 0;
#define ISQRT_INNER(shift) \
{ \
if (value >= (tmp = ((root << 1) + (1 << (shift))) << (shift))) \
{ \
root += 1 << shift; \
value -= tmp; \
} \
}
ISQRT_INNER (15);
ISQRT_INNER (14);
ISQRT_INNER (13);
ISQRT_INNER (12);
ISQRT_INNER (11);
ISQRT_INNER (10);
ISQRT_INNER ( 9);
ISQRT_INNER ( 8);
ISQRT_INNER ( 7);
ISQRT_INNER ( 6);
ISQRT_INNER ( 5);
ISQRT_INNER ( 4);
ISQRT_INNER ( 3);
ISQRT_INNER ( 2);
ISQRT_INNER ( 1);
ISQRT_INNER ( 0);
#undef ISQRT_INNER
return root;
}
もちろん、これを行うためのより速い方法とより良い方法の両方がありますが、私はそれが悲観化のかなりきれいな例だと思います。
編集:それを考えてみると、展開されたループは実際にはきちんとした悲観でもありました。バージョン管理を掘り下げて、リファクタリングの第2段階も提示できます。これは、上記よりも優れたパフォーマンスを発揮します。
unsigned long isqrt(unsigned long value)
{
unsigned long tmp = 1 << 30, root = 0;
while (tmp != 0)
{
if (value >= root + tmp) {
value -= root + tmp;
root += tmp << 1;
}
root >>= 1;
tmp >>= 2;
}
return root;
}
実装はわずかに異なりますが、これはまったく同じアルゴリズムであるため、適格であると思われます。
これは、あなたが望んでいたものよりも高いレベルにあるかもしれませんが、それを修正すること(許可されている場合)には、より高いレベルの痛みも伴います。
確立され、テストされ、成熟したライブラリを使用する代わりに、オブジェクトリレーションシップマネージャ/データアクセスレイヤーを手でロールアウトすることを主張します(指摘された後でも)。
そうしないと非常に多くのエラーが発生するため、すべての外部キー制約がデータベースから削除されました。
これは質問に完全には当てはまりませんが、とにかく警告的な話に言及します。ゆっくり実行されている分散アプリで作業していたときに、DCに飛び込んで、主に問題の解決を目的とした会議に参加しました。プロジェクトリーダーは、週末にいくつかの測定を行って、ボトルネックを単一のメソッドに限定したことを志願しましたが、ローカルルックアップにレコードがないため、アプリケーションをリモートサーバーに移動する必要がありました。レコードをローカルストアに追加し直すことで、遅延が解消され、問題は解決しました。
すべてのjavascript操作の前に、操作対象のオブジェクトが存在するかどうかを確認します。
if (myObj) { //or its evil cousin, if (myObj != null) {
label.text = myObj.value;
// we know label exists because it has already been
// checked in a big if block somewhere at the top
}
このタイプのコードに関する私の問題は、それが存在しない場合、誰も気にしないようです?何もしませんか?ユーザーにフィードバックを提供しませんか?
Object expected
エラーは迷惑ですが、これは最善の解決策ではありません。
YAGNIの過激派はどうですか。これは、時期尚早な悲観化の一形態です。 YAGNIを適用するといつでも必要になり、最初に追加した場合に比べて10倍の労力で追加するように思えます。成功したプログラムを作成する場合、あなたはそれを必要としている可能性が高いです。寿命がすぐに尽きるプログラムを作成することに慣れているなら、YAGNIを練習し続けてください。
正確には時期尚早の最適化ではありませんが、確かに見当違いです-これは、Windows 7に関する記事から、BBCのWebサイトで読みました。
Curran氏は、Microsoft Windowsチームはオペレーティングシステムのあらゆる側面を熟知して改善を行っていると述べました。 「WAVファイルのシャットダウンミュージックをわずかにトリミングすることで、シャットダウン時間を400ミリ秒短縮することができました。
今、私はまだWindows 7を試したことがないので、間違っているかもしれませんが、シャットダウンにかかる時間よりも重要な他の問題があることは間違いありません。結局、「Windowsをシャットダウンしています」というメッセージが表示されたら、モニターの電源がオフになり、立ち去ろうとしています。その400ミリ秒はどのようなメリットがありますか?
私の部署の誰かが文字列クラスを書いたことがあります。 CString
のようなインターフェースですが、Windowsに依存しません。
彼らが行った「最適化」の1つは、必要以上のメモリをnot割り当てることでした。 std::string
のようなクラスが余分なメモリを割り当てる理由は、一連の+=
操作をO(n)時間で実行できるようにするためです。
代わりに、すべての単一+=
callが再割り当てを強制し、O(n²)に繰り返しアペンドされました 画家のシュレミエルアルゴリズム 。
私の元同僚(実際には soab )が割り当てられ、Java ERP顧客のデータ(小売業界)を収集および分析しました。彼は、すべてのカレンダー/日時フィールドをそのコンポーネント(秒、分、時間、日、月、年、曜日、ビメスター、トリメスター(!)) 「毎週月曜日」を他にどのように照会しますか?」
悲観化はめったにないと思います。パフォーマンスチューニングを行った私の経験から、パフォーマンスの低下の多くは、「効率性」の名の下に正当化された「優れたプログラミングプラクティス」が原因です。例:
マップコレクションまたは「辞書」
これらは通常、ある種のハッシュコーディングを使用するため、O(1)のパフォーマンスが得られますが、通常使用されるよりもはるかに多くのアイテムで満たされた場合でも壊れます。
イテレータ
実際にそうであるかどうかを確認することはめったにないので、これらは効率的なインラインコードに最適化される可能性があると正当化されます。
データの一貫性を保つ方法としての通知とイベント処理
データ構造はめったに正規化されないため、不整合を管理する必要があり、通知は問題を「即座に」処理するため、通常の方法です。ただし、即時性と効率の間には大きな違いがあります。また、GetまたはSetの場合、「プロパティ」は一貫性を保つために、データ構造の奥深くに到達することをお勧めします。これらの「ショートリーシュ」メソッドは、大量の無駄な計算を引き起こす可能性があります。データ構造を定期的に循環して「修復」するなどの「ロングリーシュ」メソッドは、「即時」ではなく、はるかに効率的です。
早めにシステムを一目見れば、ボトルネックの可能性を指摘するのに役立つかもしれません。
「この部分は高速である必要はありません」(ログをアーカイブする)「この部分は高速である必要があります」(新しい接続を受け入れる)
その後、非常に高速な部品は通常、汚れた癖で特別に最適化する必要はなく、通常はまともなハードウェアと適切にコード化された部品で十分です。
「コードのこの部分を非常に高速にすることで何かを得られますか?」という簡単な質問に答えるだけです。素晴らしいガイドラインになります。つまり、常識を使用すると、プロジェクトの他の部分が最適化されます!
while true; do echo 3 > /proc/sys/vm/drop_caches; sleep 3600; done
これにより、カーネルはディスクキャッシュのクリアに時間を費やし、成功すると、キャッシュが再配置されるまですべてがゆっくりと実行されました。ディスクキャッシュにより、アプリケーションが使用できるメモリを使用できなかったという誤解が原因です。
誰にも違反はありませんが、私はこれを持っている課題(Java)を採点しました
import Java.lang.*;
別の派手なパフォーマンスのトリック:)
if (!loadFromDb().isEmpty) {
resultList = loadFromDb();
// do something with results
}
わずかな追加のDBヒットで、10行のコードのようにすべての時間を節約できます。おそらく、それは空のリストではほとんど何もしません。そして、このようなものはコード中に散らばっていました:)
私が何年も前にコンサルタントとして訪れたある会社は、次のようなソート関数を作成していました。
procedure sort(string[] values, string direction)
begin
while not sorted do
begin
for every value in values
begin
if direction="Ascending" then
begin
... swap values in ascending order
end
else if direction="Descending" then
begin
... swap values in descending order
end
end;
end;
end;
それは、内部ループに方向の文字列比較を伴うバブルソートアルゴリズム(それ自体は非効率的です)です!私は自分の目をほとんど信じることができず、おそらくここで速度をいくつか改善できると説明しました(結局彼らは私のクライアントだったので、これが今まで見た中で最も最適でないコードであるという事実について外交しなければなりませんでした:-)))
1人の同僚が特定のロールのページへのアクセスを確認する必要がありました-「管理者」のみ。これは彼女が書いたものです:
。
if( CurrentUser.CurrentRole == "Role1" || CurrentUser.CurrentRole == "Role2")
{
// Access denied
}
else
{
// Access granted
}
の代わりに
if( !CurrentUser.CurrentRole.equals("Admin") )
{
// access denied
}
そのため、システムに新しい役割が追加されるたびに、新しい役割はすべての機密ページにアクセスできました。
同じ同僚が、すべてのクエリのプロダクションテーブルとアーカイブテーブルにも参加しました。
既存のサーバーサイドバッチ(C++で記述)の "最適化"プロジェクトにあり、win32固有のコードと関数を使用して、ログクラス(!)を無効にするために "最適化"された私の同僚。
たぶん、ボトルネックはlogger.write(...)にありました。
多くのプログラマーはSQLを知らない、または知りたくないので、SQLを実際に使用してデータを配列に入れることを避けるために「トリック」を見つけます。配列は一部の人々を幸せにします。 (私はカーソルと配列の両方が大好きです。コーラとペプシ。)リレーショナルデータベースが遅いと文句を言う少数のオブジェクト指向プログラマーのコードで、この2つのコードブロックを見つけました。 (答えは、メモリやプロセッサを増やすことではありません。)
この場合のテーブルは、uniqueid_colが一意のIDまたは一意の行である巨大なテーブルです。
このデータをarrayXにロードします(配列はより高速でなければならないため)
super_big_tbl からuniqueid_col、col2、col3 を選択します。
(擬似コード)
Loop
arrayX.next_record
if uniqueid_col = '829-39-3984'
return col2
end if
end loop
(私の答えは一番下にあります。)
この次は、私も見た単純な間違いです。アイデアは、この方法で重複を取得しないことです。
[。
正しい構文は
super_big_tbl からuniqueid_col、col2、col3 を選択します。uniqueid_col= '829-39-3984'
小さな/非ループの文字列連結のためにStringBuilderに言及するつもりでしたが、それは言及されました。
メソッドの変数をプライベートクラスメンバに入れて、メソッドが実行されるたびにガベージコレクションが行われないようにします。変数は値型です。
整数フィールドを使用して割り当てられたアプリケーションbitwiseクライアントがユーザーを追加できるグループ化にアクセスします。当時は、合計32個のグループを作成して、500以上のすべてのクライアントで共有することができました。
ああ、ビットごとの比較は等しいより速く、結合より速いですよね?
残念ながら、私がこのコードを完全に(そしてかなり声高に)驚かせたのは著者であり、著者が私のボスのボスであることがわかりました。かなり権威主義的な男であることが判明しました。
追伸.
私はあなたが何を考えているか知っています、それは完全にバイナリ文字列であるべきでしたか? :)
私は意図的なものを持っています...私はかつてバックトラックによるソートを実装しました...ちょうど概念の証明として;))言うまでもなく、そのパフォーマンスは恐ろしいものでした。
プロファイラーツールからのトリアージレポートに基づいていない重要な最適化努力は、私から大きなWTFを獲得します。