例外がスローされない場合、try / catchブロックはパフォーマンスを低下させますか?

286
Kobi 2009-08-21 09:50.

マイクロソフトの従業員とのコードレビュー中に、try{}ブロック内のコードの大部分に出くわしました。彼女とIT担当者は、これがコードのパフォーマンスに影響を与える可能性があることを示唆しました。実際、彼らは、ほとんどのコードをtry / catchブロックの外側に配置し、重要なセクションのみをチェックする必要があることを示唆しました。Microsoftの従業員は、次のホワイトペーパーが誤ったtry / catchブロックに対して警告していると付け加えました。

調べてみると、最適化に影響を与える可能性がありますが、変数がスコープ間で共有されている場合にのみ適用されるようです。

私はコードの保守性について質問したり、適切な例外を処理したりすることさえしていません(問題のコードは間違いなくリファクタリングが必要です)。また、フロー制御に例外を使用することについても言及していません。これはほとんどの場合明らかに間違っています。これらは重要な問題です(いくつかはより重要です)が、ここでは焦点を当てていません。

例外がスローされない場合、try / catchブロックはパフォーマンスにどのように影響しますか?

12 answers

216
Ben M 2009-08-21 10:02.

確認してください。

static public void Main(string[] args)
{
    Stopwatch w = new Stopwatch();
    double d = 0;

    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        try
        {
            d = Math.Sin(1);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

    w.Stop();
    Console.WriteLine(w.Elapsed);
    w.Reset();
    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        d = Math.Sin(1);
    }

    w.Stop();
    Console.WriteLine(w.Elapsed);
}

出力:

00:00:00.4269033  // with try/catch
00:00:00.4260383  // without.

ミリ秒単位:

449
416

新しいコード:

for (int j = 0; j < 10; j++)
{
    Stopwatch w = new Stopwatch();
    double d = 0;
    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        try
        {
            d = Math.Sin(d);
        }

        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }

        finally
        {
            d = Math.Sin(d);
        }
    }

    w.Stop();
    Console.Write("   try/catch/finally: ");
    Console.WriteLine(w.ElapsedMilliseconds);
    w.Reset();
    d = 0;
    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        d = Math.Sin(d);
        d = Math.Sin(d);
    }

    w.Stop();
    Console.Write("No try/catch/finally: ");
    Console.WriteLine(w.ElapsedMilliseconds);
    Console.WriteLine();
}

新しい結果:

   try/catch/finally: 382
No try/catch/finally: 332

   try/catch/finally: 375
No try/catch/finally: 332

   try/catch/finally: 376
No try/catch/finally: 333

   try/catch/finally: 375
No try/catch/finally: 330

   try/catch/finally: 373
No try/catch/finally: 329

   try/catch/finally: 373
No try/catch/finally: 330

   try/catch/finally: 373
No try/catch/finally: 352

   try/catch/finally: 374
No try/catch/finally: 331

   try/catch/finally: 380
No try/catch/finally: 329

   try/catch/finally: 374
No try/catch/finally: 334
109
TheVillageIdiot 2009-08-30 11:04.

try / catchありとtry / catchなしのすべての統計を確認した後、好奇心から、両方のケースで何が生成されるかを確認するために後ろを振り返ることを余儀なくされました。コードは次のとおりです。

C#:

private static void TestWithoutTryCatch(){
    Console.WriteLine("SIN(1) = {0} - No Try/Catch", Math.Sin(1)); 
}

MSIL:

.method private hidebysig static void  TestWithoutTryCatch() cil managed
{
  // Code size       32 (0x20)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "SIN(1) = {0} - No Try/Catch"
  IL_0006:  ldc.r8     1.
  IL_000f:  call       float64 [mscorlib]System.Math::Sin(float64)
  IL_0014:  box        [mscorlib]System.Double
  IL_0019:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                object)
  IL_001e:  nop
  IL_001f:  ret
} // end of method Program::TestWithoutTryCatch

C#:

private static void TestWithTryCatch(){
    try{
        Console.WriteLine("SIN(1) = {0}", Math.Sin(1)); 
    }
    catch (Exception ex){
        Console.WriteLine(ex);
    }
}

MSIL:

.method private hidebysig static void  TestWithTryCatch() cil managed
{
  // Code size       49 (0x31)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Exception ex)
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldstr      "SIN(1) = {0}"
    IL_0007:  ldc.r8     1.
    IL_0010:  call       float64 [mscorlib]System.Math::Sin(float64)
    IL_0015:  box        [mscorlib]System.Double
    IL_001a:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                  object)
    IL_001f:  nop
    IL_0020:  nop
    IL_0021:  leave.s    IL_002f //JUMP IF NO EXCEPTION
  }  // end .try
  catch [mscorlib]System.Exception 
  {
    IL_0023:  stloc.0
    IL_0024:  nop
    IL_0025:  ldloc.0
    IL_0026:  call       void [mscorlib]System.Console::WriteLine(object)
    IL_002b:  nop
    IL_002c:  nop
    IL_002d:  leave.s    IL_002f
  }  // end handler
  IL_002f:  nop
  IL_0030:  ret
} // end of method Program::TestWithTryCatch

私はILの専門家ではありませんが、4行目にローカル例外オブジェクトが作成され、.locals init ([0] class [mscorlib]System.Exception ex)その後17行目までtry / catchを使用しないメソッドの場合とほとんど同じであることがわかりますIL_0021: leave.s IL_002f。例外が発生した場合、コントロールは行IL_0025: ldloc.0にジャンプしIL_002d: leave.s IL_002fます。それ以外の場合は、ラベルにジャンプし、関数が戻ります。

例外が発生しない場合は、例外オブジェクトとジャンプ命令 のみ を保持するローカル変数を作成するオーバーヘッドであると安全に推測でき ます。

66
John Kugelman 2009-08-21 10:00.

いいえ。try/ finallyブロックが実際にプログラムに測定可能な影響を与えることを妨げる些細な最適化である場合は、そもそも.NETを使用すべきではありません。

35
arul 2009-08-21 10:00.

.NET例外モデルの非常に包括的な説明。

リコ・マリアーニのパフォーマンスのヒント:例外コスト:投げるときと投げないとき

最初の種類のコストは、コードで例外処理を行うための静的コストです。ここでは、管理された例外は実際には比較的うまく機能します。つまり、静的コストはC ++で言うよりもはるかに低くなる可能性があります。どうしてこれなの?静的コストは、実際には2種類の場所で発生します。1つは、これらの構造のコードがあるtry / finally / catch / throwの実際のサイトです。次に、管理されていないコードでは、例外がスローされた場合に破棄する必要があるすべてのオブジェクトを追跡することに関連するステルスコストがあります。存在しなければならないクリーンアップロジックはかなりの量あり、卑劣な部分は、それ自体がスローまたはキャッチしない、あるいは例外を明白に使用しないコードでさえ、それ自体の後でクリーンアップする方法を知る負担を負うということです。

Dmitriy Zaslavskiy:

Chris Brummeのメモによると、キャッチが存在する場合、一部の最適化がJITによって実行されていないという事実に関連するコストもあります。

27
awe 2009-08-25 23:28.

例ではBenMとは構造が異なります。これは、内側のforループ内でオーバーヘッドが拡張されるため、2つのケースをうまく比較できなくなります。

以下は、チェックするコード全体(変数宣言を含む)がTry / Catchブロック内にある場合の比較のためにより正確です。

        for (int j = 0; j < 10; j++)
        {
            Stopwatch w = new Stopwatch();
            w.Start();
            try { 
                double d1 = 0; 
                for (int i = 0; i < 10000000; i++) { 
                    d1 = Math.Sin(d1);
                    d1 = Math.Sin(d1); 
                } 
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString()); 
            }
            finally { 
                //d1 = Math.Sin(d1); 
            }
            w.Stop(); 
            Console.Write("   try/catch/finally: "); 
            Console.WriteLine(w.ElapsedMilliseconds); 
            w.Reset(); 
            w.Start(); 
            double d2 = 0; 
            for (int i = 0; i < 10000000; i++) { 
                d2 = Math.Sin(d2);
                d2 = Math.Sin(d2); 
            } 
            w.Stop(); 
            Console.Write("No try/catch/finally: "); 
            Console.WriteLine(w.ElapsedMilliseconds); 
            Console.WriteLine();
        }

Ben Mから元のテストコードを実行したとき、デバッグ構成とリリース構成の両方に違いがあることに気付きました。

このバージョンでは、デバッグバージョンの違い(実際には他のバージョンよりも多い)に気づきましたが、リリースバージョンでは違いはありませんでした。

結論
これらのテストに基づいて、Try / Catchパフォーマンスにわずかな影響を与えると言えると思います。

編集:
ループ値を10000000から1000000000に増やしようとしましたが、リリースで再度実行してリリースにいくつかの違いを取得しました。結果は次のとおりです。

   try/catch/finally: 509
No try/catch/finally: 486

   try/catch/finally: 479
No try/catch/finally: 511

   try/catch/finally: 475
No try/catch/finally: 477

   try/catch/finally: 477
No try/catch/finally: 475

   try/catch/finally: 475
No try/catch/finally: 476

   try/catch/finally: 477
No try/catch/finally: 474

   try/catch/finally: 475
No try/catch/finally: 475

   try/catch/finally: 476
No try/catch/finally: 476

   try/catch/finally: 475
No try/catch/finally: 476

   try/catch/finally: 475
No try/catch/finally: 474

結果が結果的でないことがわかります。場合によっては、Try / Catchを使用したバージョンの方が実際に高速です。

15
Guffa 2009-08-21 10:14.

try..catchタイトループでの実際の影響をテストしましたが、それ自体では小さすぎて、通常の状況でのパフォーマンスの問題にはなりません。

ループがほとんど機能しない場合(私のテストではx++)、例外処理の影響を測定できます。例外処理を伴うループの実行には、約10倍の時間がかかりました。

ループが実際の作業を行う場合(私のテストではInt32.Parseメソッドを呼び出しました)、例外処理の影響は小さすぎて測定できません。ループの順序を入れ替えることで、はるかに大きな違いが得られました...

12
RHicke 2009-08-21 10:29.

キャッチブロックがパフォーマンスに与える影響はごくわずかですが、例外スローはかなり大きくなる可能性があります。これはおそらく同僚が混乱した場所です。

9
Ted Oddman 2019-11-28 21:17.

予防は取り扱いよりも優れている」とはいえ、パフォーマンスと効率の観点から、事前検証よりもトライキャッチを選択することができました。以下のコードを検討してください。

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 1; i < int.MaxValue; i++)
{
    if (i != 0)
    {
        int k = 10 / i;
    }
}
stopwatch.Stop();
Console.WriteLine($"With Checking: {stopwatch.ElapsedMilliseconds}"); stopwatch.Reset(); stopwatch.Start(); for (int i = 1; i < int.MaxValue; i++) { try { int k = 10 / i; } catch (Exception) { } } stopwatch.Stop(); Console.WriteLine($"With Exception: {stopwatch.ElapsedMilliseconds}");

結果は次のとおりです。

With Checking:  20367
With Exception: 13998
8
Isaac 2009-08-30 09:51.

try / catchはパフォーマンスに影響を与えます。

しかし、それは大きな影響ではありません。try / catchの複雑さは、ループに配置される場合を除いて、単純な割り当てと同じように、一般にO(1)です。したがって、それらを賢く使用する必要があります。

これは、try / catchのパフォーマンスに関するリファレンスです(ただし、その複雑さについては説明していませんが、暗黙的に示されています)。Throw FewerExceptionsセクションをご覧ください

6
supercat 2014-08-29 06:11.

理論的には、try / catchブロックは、例外が実際に発生しない限り、コードの動作に影響を与えません。ただし、try / catchブロックの存在が大きな影響を与える可能性があるまれな状況や、影響が目立つ可能性がある、まれではあるがほとんどわかりにくい状況がいくつかあります。この理由は、次のような特定のコードです。

Action q;
double thing1()
  { double total; for (int i=0; i<1000000; i++) total+=1.0/i; return total;}
double thing2()
  { q=null; return 1.0;}
...
x=thing1();     // statement1
x=thing2(x);    // statement2
doSomething(x); // statement3

コンパイラは、statement2がstatement3の前に実行されることが保証されているという事実に基づいて、statement1を最適化できる場合があります。コンパイラがthing1に副作用がなく、thing2が実際にxを使用していないことを認識できる場合は、thing1を完全に省略しても問題ありません。[この場合のように] thing1が高価な場合、それは主要な最適化になる可能性がありますが、thing1が高価な場合も、コンパイラーが最適化する可能性が最も低い場合です。コードが変更されたとします。

x=thing1();      // statement1
try
{ x=thing2(x); } // statement2
catch { q(); }
doSomething(x);  // statement3

これで、statement2を実行せずにstatement3を実行できる一連のイベントが存在します。のコードにthing2例外をスローできるものがない場合でも、別のスレッドがを使用して、クリアされたことをInterlocked.CompareExchange通知してqに設定しThread.ResetAbortThread.Abort()beforeステートメント2がその値をに書き込む前に実行する可能性がありますx。次に、catchThread.ResetAbort()[デリゲートを介してq]実行され、statement3で実行を続行できるようになります。もちろん、このような一連のイベントは非常に起こりそうにありませんが、このような起こりそうもないイベントが発生した場合でも、仕様に従って機能するコードを生成するにはコンパイラーが必要です。

一般に、コンパイラーは、複雑なコードよりも単純なコードを除外する機会に気付く可能性がはるかに高いため、例外がスローされない場合、try / catchがパフォーマンスに大きな影響を与えることはめったにありません。それでも、try / catchブロックの存在により最適化が妨げられる場合がありますが、try / catchの場合は、コードをより高速に実行できます。

4
Ira Baxter 2009-08-31 22:20.

try / catchブロックがどのように機能するか、および例外が発生しない場合にオーバーヘッドが高い実装とオーバーヘッドがゼロの実装については、try / catchの実装に関する説明を参照してください。特に、Windows 32ビットの実装はオーバーヘッドが高いと思いますが、64ビットの実装はそうではありません。

2
l33t 2020-10-07 05:42.

はい、try/catchパフォーマンスを「損なう」でしょう(すべてが相対的です)。無駄なCPUサイクルに関してはそれほど多くはありませんが、考慮すべき他の重要な側面があります。

  • コードサイズ
  • メソッドのインライン化

基準

まず、いくつかの洗練されたツール(BenchmarkDotNetなど)を使用して速度を確認しましょう。としてコンパイルされRelease (AnyCPU)x64マシン上で実行されます。テストでは実際にそれNoTryCatch()が非常に速く、少し速いことがわかりますが、違いはないと思います。

|            Method |   N |     Mean |     Error |    StdDev |
|------------------ |---- |---------:|----------:|----------:|
|        NoTryCatch | 0.5 | 3.770 ns | 0.0492 ns | 0.0411 ns |
|      WithTryCatch | 0.5 | 4.060 ns | 0.0410 ns | 0.0384 ns |
| WithTryCatchThrow | 0.5 | 3.924 ns | 0.0994 ns | 0.0881 ns |

分析

いくつかの追加のメモ。

|            Method | Code size | Inlineable |
|------------------ |---------- |-----------:|
|        NoTryCatch |        12 |        yes |
|      WithTryCatch |        18 |          ? |
| WithTryCatchThrow |        18 |         no |

コードサイズNoTryCatch()はコードで12バイトになりますが、try / catchはさらに6バイトを追加します。また、を書くときはいつでも、try/catchおそらく1つ以上のthrow new Exception("Message", ex)ステートメントがあり、コードをさらに「肥大化」させます。

ただし、ここで最も重要なことは、コードのインライン化です。.NET単なる存在throwキーワードは、メソッドは、コンパイラによってインライン化されることはありませんことを意味し(遅くコードを暗示するだけでなく、より少ないフットプリント)。私は最近この事実を徹底的にテストしたので、それはまだで有効であるようです.NET Coretry/catch同じルールに従うかどうかわからない。TODO: Verify!

完全なテストコード

using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace TryCatchPerformance
{
    public class TryCatch
    {
        [Params(0.5)]
        public double N { get; set; }

        [Benchmark]
        public void NoTryCatch() => Math.Sin(N);

        [Benchmark]
        public void WithTryCatch()
        {
            try
            {
                Math.Sin(N);
            }
            catch
            {
            }
        }

        [Benchmark]
        public void WithTryCatchThrow()
        {
            try
            {
                Math.Sin(N);
            }
            catch (Exception ex)
            {
                throw;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<TryCatch>();
        }
    }
}

Related questions

MORE COOL STUFF

「水曜日」シーズン1の中心には大きなミステリーがあります

「水曜日」シーズン1の中心には大きなミステリーがあります

Netflixの「水曜日」は、典型的な10代のドラマ以上のものであり、実際、シーズン1にはその中心に大きなミステリーがあります.

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ケイト・ミドルトンは、州の夕食会と州の訪問中にカミラ・パーカー・ボウルズからスポットライトを奪いたくなかった、と専門家は言う.

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンが、オリビア・ワイルドが彼女とハリー・スタイルズとの間の「難しい」が「非常に友好的」な分割を恒久的にすることを望んでいる理由を見つけてください.

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする 

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする&nbsp;

エリザベス女王の死後、ケイト・ミドルトンが舞台裏で「非常に困難な時期」を過ごしていたと伝えられている理由を調べてください.

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セント ヘレナ島のジェイコブズ ラダーは 699 段の真っ直ぐ上る階段で、頂上に到達すると証明書が発行されるほどの難易度です。

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

パンデミックは終わったかもしれないが、Covid-19 は終わっていない

パンデミックは終わったかもしれないが、Covid-19 は終わっていない

2021 年 6 月 8 日にニューヨーク市で開催された covid-19 パンデミックで亡くなった人々の命を偲び、祝うために、ネーミング ザ ロスト メモリアルズが主催するイベントと行進の最中に、グリーンウッド墓地の正門から記念碑がぶら下がっています。週末、ジョー・バイデン大統領は、covid-19 パンデミックの終息を宣言しました。これは、過去 2 年以上にわたり、公の場でそうするための長い列の中で最新のものです。

デビル・イン・オハイオの予告編は、エミリー・デシャネルもオハイオにいることを明らかにしています

デビル・イン・オハイオの予告編は、エミリー・デシャネルもオハイオにいることを明らかにしています

オハイオ州のエミリー・デシャネル みんな早く来て、ボーンズが帰ってきた!まあ、ショーボーンズではなく、彼女を演じた俳優. エミリー・デシャネルに最後に会ってからしばらく経ちました.Emily Deschanel は、長期にわたるプロシージャルな Bones の Temperance “Bones” Brennan としてよく知られています。

ドナルド・トランプはFBIのマー・ア・ラーゴ襲撃映像をリリースする予定ですか?

ドナルド・トランプはFBIのマー・ア・ラーゴ襲撃映像をリリースする予定ですか?

どうやら、ドナルド・トランプに近い人々は、今月初めにFBIによって家宅捜索された彼のMar-a-Lago財産からの映像を公開するよう彼に勧めています. 前大統領はテープを公開するかどうかを確認していませんが、息子はフォックス・ニュースにそうなるだろうと語った.

Andor は、他の Star Wars ショーから大きな距離を置きます。

Andor は、他の Star Wars ショーから大きな距離を置きます。

アンドールの一場面。数十年前、ジョージ・ルーカスがスター・ウォーズのテレビ番組を制作するのを妨げた主な理由は、お金でした。

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、子供向けのパズルの本の序文を書き、ジョージ王子、シャーロット王女、ルイ王子と一緒にテキストを読むと述べた.

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

Yak's Produce は、数十個のつぶれたメロンを野生動物のリハビリ専門家であるレスリー グリーンと彼女のルイジアナ州の救助施設で暮らす 42 匹の動物に寄付しました。

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

8 枚目のスタジオ アルバムのリリースに向けて準備を進めているデミ ロヴァートは、「スーパー グレート ガイ」と付き合っている、と情報筋は PEOPLE に確認しています。

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

イーサン プラスの誕生日のお祝いは、TLC のウェルカム トゥ プラスビルのシーズン 4 のフィナーレで、戦争中の母親のキム プラスと妻のオリビア プラスを結びつけました。

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

ホームオフィスのセットアップ術を極めよう!AppExert の開発者は、家族全員が一緒にいる場合でも、在宅勤務の技術を習得しています。祖父や曽祖父が共同家族で暮らしていた頃の記憶がよみがえりました。

2022 年、私たちのデジタル ライフはどこで終わり、「リアル ライフ」はどこから始まるのでしょうか?

20 年前のタイムトラベラーでさえ、日常生活におけるデジタルおよびインターネットベースのサービスの重要性に驚くことでしょう。MySpace、eBay、Napster などのプラットフォームは、高速化に焦点を合わせた世界がどのようなものになるかを示してくれました。

ニューロマーケティングの秘密科学

ニューロマーケティングの秘密科学

マーケティング担当者が人間の欲望を操作するために使用する、最先端の (気味が悪いと言う人もいます) メソッドを探ります。カートをいっぱいにして 3 桁の領収書を持って店を出る前に、ほんの数点の商品を買いに行ったことはありませんか? あなたは一人じゃない。

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

私たちが住んでいる世界を確実に理解するには、データが必要です。ただし、空間参照がない場合、このデータは地理的コンテキストがないと役に立たなくなる可能性があります。

Language