クロスプラットフォームのパフォーマンスCross-Platform Performance

アプリケーションのパフォーマンス低下は、さまざまな挙動で現れます。Poor application performance presents itself in many ways. たとえば、アプリケーションが応答しなかったり、スクロールが遅くなったり、バッテリーの寿命が縮まったりすることがあります。It can make an application seem unresponsive, can cause slow scrolling, and can reduce battery life. ただし、パフォーマンスを最適化するには、単に効率的なコードを実装するだけでは不十分です。However, optimizing performance involves more than just implementing efficient code. アプリケーション パフォーマンスに関わるユーザー エクスペリエンスも考慮する必要があります。The user's experience of application performance must also be considered. たとえば、操作の実行によって、ユーザーが他の操作を実行できない状況にならないようにすることで、ユーザー エクスペリエンスを改善できます。For example, ensuring that operations execute without blocking the user from performing other activities can help to improve the user's experience.

プロファイラーを使用するUse the Profiler

アプリケーションを開発する場合、プロファイリング後にコードの最適化を試みることが重要です。When developing an application, it's important to only attempt to optimize code once it has been profiled. プロファイリングは、パフォーマンスの問題を減らすために、コードの最適化で最も大きな効果がある場所を特定するための手法です。Profiling is a technique for determining where code optimizations will have the greatest effect in reducing performance problems. プロファイラーは、アプリケーションのメモリ使用量を追跡し、アプリケーションのメソッドの実行時間を記録します。The profiler tracks the application's memory usage, and records the running time of methods in the application. このデータは、最適化の絶好の機会を見つけられるように、アプリケーションの実行パスと、コードの実行コストをナビゲートする場合に役立ちます。This data helps to navigate through the execution paths of the application, and the execution cost of the code, so that the best opportunities for optimization can be discovered.

Xamarin Profiler では、アプリケーションでのパラメーターに関する問題の測定、評価、および検出が可能です。The Xamarin Profiler will measure, evaluate, and help find performance-related issues in an application. Visual Studio for Mac または Visual Studio 内から Xamarin.iOS および Xamarin.Android アプリケーションのプロファイリングを行う場合に使用できます。It can be used to profile Xamarin.iOS and Xamarin.Android applications from inside Visual Studio for Mac or Visual Studio. Xamarin Profiler の詳細については、「Xamarin Profiler の概要」を参照してください。For more information about the Xamarin Profiler, see Introduction to the Xamarin Profiler.

次のベスト プラクティスは、アプリのプロファイリングを行う場合に推奨されます。The following best practices are recommended when profiling an app:

  • シミュレーターはアプリケーションのパフォーマンスを損なう可能性があるため、シミュレーターではアプリケーションのプロファイリングは行わないでください。Avoid profiling an application in a simulator, as the simulator may distort the application performance.
  • 1 つのデバイスでパフォーマンスを測定する際に、必ずしも他のデバイスのパフォーマンス特性が示されるわけではないため、さまざまなデバイスでプロファイリングを実行するのが理想的です。Ideally, profiling should be performed on a variety of devices, as taking performance measurements on one device won't always show the performance characteristics of other devices. ただし、少なくとも、性能が最も低いと思われるデバイスでプロファイリングを行う必要があります。However, at a minimum, profiling should be performed on a device that has the lowest anticipated specification.
  • 他のすべてのアプリケーションを終了し、他のアプリケーションではなく、プロファイリングを行うアプリケーションのあらゆる影響を測定するようにしてください。Close all other applications to ensure that the full impact of the application being profiled is being measured, rather than the other applications.

IDisposable のリソースを解放するRelease IDisposable Resources

IDisposable インターフェイスは、リソースを解放するためのメカニズムを提供します。The IDisposable interface provides a mechanism for releasing resources. リソースを明示的に解放するために実装する必要がある Dispose メソッドを提供します。It provides a Dispose method that should be implemented to explicitly release resources. IDisposable はデストラクターではありません。以下の状況でのみ実装する必要があります。IDisposable is not a destructor, and should only be implemented in the following circumstances:

  • クラスがアンマネージ リソースを所有している場合。When the class owns unmanaged resources. 解放が必要な代表的なアンマネージ リソースには、ファイル、ストリーム、およびネットワーク接続が含まれます。Typical unmanaged resources that require releasing include files, streams, and network connections.
  • クラスがマネージ IDisposable リソースを所有している場合。When the class owns managed IDisposable resources.

使用者は、インスタンスが必要でなくなったときに、 IDisposable.Dispose 実装を呼び出してリソースを解放できます。Type consumers can then call the IDisposable.Dispose implementation to free resources when the instance is no longer required. これには、次の 2 つのアプローチがあります。There are two approaches for achieving this:

  • using ステートメントで IDisposable オブジェクトをラップする。By wrapping the IDisposable object in a using statement.
  • try/finally ブロックで IDisposable.Dispose 呼び出しをラップする。By wrapping the call to IDisposable.Dispose in a try/finally block.

using ステートメントでの IDisposable オブジェクトのラップWrapping the IDisposable Object in a using Statement

以下のコード例は、using ステートメントでの IDisposable オブジェクトのラップ方法を示しています。The following code example shows how to wrap an IDisposable object in a using statement:

public void ReadText (string filename)
{
  ...
  string text;
  using (StreamReader reader = new StreamReader (filename)) {
    text = reader.ReadToEnd ();
  }
  ...
}

StreamReader クラスは IDisposable を実装し、using ステートメントは、スコープを外れる前に StreamReader オブジェクトで StreamReader.Dispose メソッドを呼び出す便利な構文を提供します。The StreamReader class implements IDisposable, and the using statement provides a convenient syntax that calls the StreamReader.Dispose method on the StreamReader object prior to it going out of scope. StreamReader オブジェクトは、using ブロック内では読み取り専用です。再割り当てすることはできません。Within the using block, the StreamReader object is read-only and cannot be reassigned. また、コンパイラは try/finally ブロックの中間言語 (IL) を実装するため、例外が発生した場合でも、using ステートメントで確実に Dispose メソッドを呼び出すことができます。The using statement also ensures that the Dispose method is called even if an exception occurs, as the compiler implements the intermediate language (IL) for a try/finally block.

Try/Finally ブロックでの IDisposable.Dispose 呼び出しのラップWrapping the Call to IDisposable.Dispose in a Try/Finally Block

以下のコード例は、try/finally ブロックでの IDisposable.Dispose 呼び出しのラップ方法を示しています。The following code example shows how to wrap the call to IDisposable.Dispose in a try/finally block:

public void ReadText (string filename)
{
  ...
  string text;
  StreamReader reader = null;
  try {
    reader = new StreamReader (filename);
    text = reader.ReadToEnd ();
  } finally {
    if (reader != null) {
      reader.Dispose ();
    }
  }
  ...
}

StreamReader クラスは IDisposable を実装し、finally ブロックはリソースを解放するために StreamReader.Dispose メソッドを呼び出します。The StreamReader class implements IDisposable, and the finally block calls the StreamReader.Dispose method to release the resource.

詳細については、IDisposable インターフェイスに関するページを参照してください。For more information, see IDisposable Interface.

イベントのサブスクリプションを解除するUnsubscribe from Events

メモリ リークを防ぐため、サブスクライバー オブジェクトが破棄される前に、イベントのサブスクリプションを解除する必要があります。To prevent memory leaks, events should be unsubscribed from before the subscriber object is disposed of. イベントのサブスクリプションを解除するまで、パブリッシュ側オブジェクトでイベントのデリゲートは、サブスクライバーのイベント ハンドラーをカプセル化するデリゲートへの参照を保持しています。Until the event is unsubscribed from, the delegate for the event in the publishing object has a reference to the delegate that encapsulates the subscriber's event handler. パブリッシュ側オブジェクトがこの参照を保持している限り、ガベージ コレクションはサブスクライバー オブジェクトのメモリを再利用しません。As long as the publishing object holds this reference, garbage collection will not reclaim the subscriber object memory.

以下のコード例は、イベントのサブスクリプションの解除方法を示しています。The following code example shows how to unsubscribe from an event:

public class Publisher
{
  public event EventHandler MyEvent;

  public void OnMyEventFires ()
  {
    if (MyEvent != null) {
      MyEvent (this, EventArgs.Empty);
    }
  }
}

public class Subscriber : IDisposable
{
  readonly Publisher publisher;

  public Subscriber (Publisher publish)
  {
    publisher = publish;
    publisher.MyEvent += OnMyEventFires;
  }

  void OnMyEventFires (object sender, EventArgs e)
  {
    Debug.WriteLine ("The publisher notified the subscriber of an event");
  }

  public void Dispose ()
  {
    publisher.MyEvent -= OnMyEventFires;
  }
}

Subscriber クラスは、自身の Dispose メソッドでイベントのサブスクリプションを解除します。The Subscriber class unsubscribes from the event in its Dispose method.

また、ラムダ式ではオブジェクトを参照して保持できるため、イベント ハンドラーとラムダ構文を使用する場合、参照サイクルが発生することがあります。Reference cycles can also occur when using event handlers and lambda syntax, as lambda expressions can reference and keep objects alive. したがって、匿名メソッドへの参照をフィールドに格納し、次のコード例のように、イベントのサブスクリプションの解除で使用できます。Therefore, a reference to the anonymous method can be stored in a field and used to unsubscribe from the event, as shown in the following code example:

public class Subscriber : IDisposable
{
  readonly Publisher publisher;
  EventHandler handler;

  public Subscriber (Publisher publish)
  {
    publisher = publish;
    handler = (sender, e) => {
      Debug.WriteLine ("The publisher notified the subscriber of an event");
    };
    publisher.MyEvent += handler;
  }

  public void Dispose ()
  {
    publisher.MyEvent -= handler;
  }
}

handler フィールドは匿名メソッドへの参照を保持し、イベントのサブスクリプションとサブスクリプション解除に使用されます。The handler field maintains the reference to the anonymous method, and is used for event subscription and unsubscribe.

弱参照を使用して不変オブジェクトを回避するUse Weak References to Prevent Immortal Objects

注意

iOS 開発者は、アプリでメモリを効率的に使用できるように、iOS での循環参照の回避に関するドキュメントを確認する必要があります。iOS developers should review the documentation on avoiding circular references in iOS to ensure their apps use memory efficiently.

オブジェクトの作成コストの発生を遅らせるDelay the Cost of Creating Objects

遅延初期化を使用して、最初に使用されるまでオブジェクトの作成を遅らせることができます。Lazy initialization can be used to defer the creation of an object until it's first used. この手法は主に、パフォーマンスの改善、計算の回避、メモリ要件の縮小を目的として利用されます。This technique is primarily used to improve performance, avoid computation, and reduce memory requirements.

以下の 2 つのシナリオの場合は作成コストのかかるオブジェクトに対して遅延初期化を使用することを検討してください。Consider using lazy initialization for objects that are expensive to create in this two scenarios:

  • アプリケーションでオブジェクトを使用しない可能性がある。The application might not use the object.
  • オブジェクトが作成される前に、コストのかかる他の操作を完了する必要がある。Other expensive operations must complete before the object is created.

以下のコード例に示すように、遅延初期化の型を定義する場合は、Lazy<T> クラスを使用します。The Lazy<T> class is used to define a lazy-initialized type, as demonstrated in the following code example:

void ProcessData(bool dataRequired = false)
{
  Lazy<double> data = new Lazy<double>(() =>
  {
    return ParallelEnumerable.Range(0, 1000)
                 .Select(d => Compute(d))
                 .Aggregate((x, y) => x + y);
  });

  if (dataRequired)
  {
    if (data.Value > 90)
    {
      ...
    }
  }
}

double Compute(double x)
{
  ...
}

遅延初期化は、Lazy<T>.Value プロパティへの初回のアクセス時に発生します。Lazy initialization occurs the first time the Lazy<T>.Value property is accessed. 初回アクセス時にラップされた型が作成され、返されて、今後のアクセスのために保存されます。The wrapped type is created and returned on first access, and stored for any future access.

遅延初期化の詳細については、「遅延初期化」を参照してください。For more information about lazy initialization, see Lazy Initialization.

非同期操作を実装するImplement Asynchronous Operations

.NET は、非同期バージョンの多くの API を提供します。.NET provides asynchronous versions of many of its APIs. 同期 API とは異なり、非同期 API は、アクティブな実行スレッドが、呼び出しスレッドを長時間ブロックすることはありません。Unlike synchronous APIs, the asynchronous APIs ensure that the active execution thread never blocks the calling thread for a significant amount of time. したがって、UI スレッドから API を呼び出すときは、非同期 API (使用可能な場合) を使用してください。Therefore, when calling an API from the UI thread, use the asynchronous API if it's available. これにより、UI スレッドの非ブロック状態が保たれ、アプリケーションのユーザー エクスペリエンスの向上に役立ちます。This will keep the UI thread unblocked, which will help to improve the user's experience with the application.

さらに、UI スレッドがブロックされないようにするために、長時間実行される操作はバックグラウンド スレッドで実行する必要があります。In addition, long running operations should be executed on a background thread, to avoid blocking the UI thread. .NET では async および await キーワードが提供されます。これにより、バックグラウンド スレッドで長時間実行される操作を行い、完了時に結果にアクセスする非同期コードを書き込むことができます。.NET provides the async and await keywords that enable the writing of asynchronous code that executes long running operations on a background thread, and accesses the results on completion. ただし、長時間実行される操作を await キーワードで非同期に実行することはできますが、バックグラウンド スレッドでの操作の実行は保証されません。However, while long running operations can be executed asynchronously with the await keyword, this does not guarantee that the operation will run on a background thread. 代わりに、以下のコード例のように、長時間実行される操作を Task.Run に渡して、これを行うことができます。Instead, this can be accomplished by passing the long running operation to Task.Run, as shown in the following code example:

public class FaceDetection
{
  ...
  async void RecognizeFaceButtonClick(object sender, EventArgs e)
  {
    await Task.Run(() => RecognizeFace ());
    ...
  }

  async Task RecognizeFace()
  {
    ...
  }
}

RecognizeFace メソッドはバックグラウンド スレッドで実行され、RecognizeFaceButtonClick メソッドが RecognizeFace メソッドの完了を待ってから続行されます。The RecognizeFace method executes on a background thread, with the RecognizeFaceButtonClick method waiting until the RecognizeFace method completes before continuing.

長時間実行される操作ではキャンセルもサポートすべきです。Long running operations should also support cancellation. たとえば、ユーザーがアプリケーション内を移動する場合、長時間実行される操作の続行は不要になる可能性があります。For example, continuing a long running operation may become unnecessary if the user navigates within the application. キャンセルを実装するためのパターンは次のとおりです。The pattern for implementing cancellation is as follows:

  • CancellationTokenSource インスタンスを作成します。Create a CancellationTokenSource instance. このインスタンスはキャンセル通知の管理と送信を行います。This instance will manage and send cancellation notifications.
  • CancellationTokenSource.Token プロパティの値を、キャンセル可能な各タスクに渡します。Pass the CancellationTokenSource.Token property value to each task that should be cancelable.
  • それぞれのタスクに対し、キャンセルに応答するメカニズムを提供します。Provide a mechanism for each task to respond to cancellation.
  • キャンセル通知を提供する CancellationTokenSource.Cancel メソッドを呼び出します。Call the CancellationTokenSource.Cancel method to provide cancellation notification.

重要

CancellationTokenSource クラスは IDisposable インターフェイスを実装します。そのため、CancellationTokenSource インスタンスが終了したら CancellationTokenSource.Dispose メソッドを呼び出す必要があります。The CancellationTokenSource class implements the IDisposable interface, and so the CancellationTokenSource.Dispose method should be invoked once the CancellationTokenSource instance is finished with.

詳細については、「非同期サポートの概要」を参照してください。For more information, see Async Support Overview.

SGen ガベージ コレクターを使用するUse the SGen Garbage Collector

使用されなくなったオブジェクトに割り当てられているメモリを再利用するために、C# などのマネージド言語ではガーベジ コレクションが使用されます。Managed languages such as C# use garbage collection to reclaim memory that is allocated to objects that are no longer in use. Xamarin プラットフォームで使用される 2 つのガベージ コレクターは次のとおりです。The two garbage collectors used by the Xamarin platform are:

  • SGen – これは世代別ガベージ コレクターであり、Xamarin のプラットフォームの既定のガベージ コレクターです。SGen – This is a generational garbage collector and is the default garbage collector on the Xamarin platform.
  • Boehm – これは、保守的な、非世代別ガベージ コレクターです。Boehm – This is a conservative, non-generational garbage collector. これは、Classic API を使用する Xamarin.iOS アプリケーションで使用される既定のガベージ コレクターです。It is the default garbage collector used for Xamarin.iOS applications that use the Classic API.

SGen では、オブジェクトにスペースを割り当てる際に、次の 3 つのヒープのいずれかが利用されます。SGen utilizes one of three heaps to allocate space for objects:

  • 新世代 – 新しい小さなオブジェクトが割り当てられます。The Nursery – This is where new small objects are allocated. 新世代のスペースが不足すると、マイナー ガベージ コレクションが発生します。When the nursery runs out of space, a minor garbage collection will occur. ライブ オブジェクトはすべてメジャー ヒープに移動されます。Any live objects will be moved to the major heap.
  • メジャー ヒープ – 長時間実行されるオブジェクトが保持されます。Major Heap – This is where long running objects are kept. メジャー ヒープに十分なメモリがない場合は、メジャー ガベージ コレクションが発生します。If there is not enough memory in the major heap, then a major garbage collection will occur. メジャー ガベージ コレクションで十分なメモリを解放できない場合は、SGen がシステムにメモリの追加を要求します。If a major garbage collection fails to free up enough memory then SGen will ask the system for more memory.
  • ラージ オブジェクト スペース – 8000 を超えるバイトが必要なオブジェクトが保持されます。Large Object Space – This is where objects that require more than 8000 bytes are kept. ラージ オブジェクトは新世代では開始されませんが、代わりにこのヒープで割り当てられます。Large objects will not start out in the nursery, but instead will be allocated in this heap.

SGen の利点の 1 つは、マイナー ガベージ コレクションの実行にかかる時間が、最後のマイナー ガベージ コレクション以降に作成された新しいライブ オブジェクトの数に比例することです。One of the advantages of SGen is that the time it takes to perform a minor garbage collection is proportional to the number of new live objects that were created since the last minor garbage collection. したがって、これらのマイナー ガベージ コレクションの実行にかかる時間がメジャー ガベージ コレクションより短くなるため、アプリケーションのパフォーマンス上のガベージ コレクションの影響が軽減されます。This will reduce the impact of garbage collection on the performance of an application, as these minor garbage collections will take less time than a major garbage collection. メジャー ガベージ コレクションは引き続き発生しますが、頻度は低くなります。Major garbage collections will still occur, but less frequently.

SGen ガベージ コレクターは、Xamarin.iOS 9.2.1 以上の既定であるため、自動的に使用されます。The SGen garbage collector is the default in Xamarin.iOS 9.2.1 and greater, and therefore it will be used automatically. 新しいバージョンの Visual Studio からは、ガベージ コレクターを変更する機能が削除されていることに注意してください。Please note that the ability to change garbage collector has been removed from newer versions of Visual Studio. 詳細については、新しい参照カウント システムに関する記事を参照してください。For more information, see New Reference Counting System.

ガベージ コレクターの負荷の軽減Reducing Pressure on the Garbage Collector

SGen がガベージ コレクションを開始すると、メモリの再利用中は、アプリケーションのスレッドが停止します。When SGen starts a garbage collection, it will stop the application’s threads while it reclaims memory. メモリが再利用されている間、アプリケーションはしばらく一時停止するか、UI の途切れが生じる可能性があります。While memory is being reclaimed, the application may experience a brief pause or stutter in the UI. この一時停止がどう認識されるかは、次の 2 つの要因によって決まります。How perceptible this pause is depends on two factors:

  1. 頻度 – ガベージ コレクションの発生頻度。Frequency – How often garbage collection occurs. コレクション間でのメモリの割り当て量が多いほど、ガベージ コレクションの頻度が増えます。The frequency of garbage collections will increase as more memory is allocated between collections.
  2. 期間 – 個々のガベージ コレクションにかかる時間。Duration – How long each individual garbage collection will take. これは、収集されるライブ オブジェクトの数にほぼ比例します。This is roughly proportional to the number of live objects that are being collected.

つまり、多くのオブジェクトが割り当てられても、保持されなければ、多数の短いガベージ コレクションが発生します。Collectively this means that if many objects are allocated but do not stay alive, there will be many short garbage collections. 逆に、新しいオブジェクトが徐々に割り当てられ、保持されれば、少数の長いガベージ コレクションが発生します。Conversely, if new objects are allocated slowly and the objects stay alive, there will be fewer but longer garbage collections.

ガベージ コレクターの負荷を減らすには、次のガイドラインに従います。To reduce pressure on the garbage collector, follow these guidelines:

  • オブジェクト プールを使用して、短いループでのガベージ コレクションを回避します。Avoid garbage collection in tight loops by using object pools. これは特に、事前に大部分のオブジェクトを作成する必要がある、ゲームに適しています。This is particularly relevant for games, which need to create the majority of their objects in advance.
  • ストリーム、ネットワーク接続、大量のメモリ、およびファイルなどのリソースを必要でなくなった時点で明示的に解放します。Explicitly release resources such as streams, network connections, large blocks of memory, and files once they are no longer required. 詳細については、「IDisposable のリソースを解放する」を参照してください。For more information, see Release IDisposable Resources.
  • オブジェクトを収集できるように、不要になった時点でイベント ハンドラーの登録を解除します。De-register event handlers once they are no longer required, to make objects collectable. 詳細については、「イベントのサブスクリプションを解除する」を参照してください。For more information, see Unsubscribe from Events.

アプリケーションのサイズを縮小するReduce the Size of the Application

アプリケーションの実行可能ファイルのサイズの取得元を把握するために、各プラットフォームでのコンパイル処理を理解することが重要です。It's important to understand the compilation process on each platform, to understand where an application's executable size comes from:

  • iOS アプリケーションは、ARM アセンブリ言語に AOT (Ahead-of-Time) コンパイルされます。iOS applications are ahead-of-time (AOT) compiled to ARM assembly language. .NET Framework が組み込まれ、その未使用のクラスは、適切なリンカー オプションが有効になっている場合にのみ削除されます。The .NET framework is included, with unused classes being stripped out only if the appropriate linker option is enabled.
  • Android アプリケーションは中間言語 (IL) にコンパイルされ、MonoVM と Just-In-Time (JIT) コンパイルでパッケージ化されます。Android applications are compiled to intermediate language (IL) and packaged with MonoVM and just-in-time (JIT) compilation. 未使用のフレームワーク クラスは、適切なリンカー オプションが有効になっている場合にのみ削除されます。Unused framework classes are stripped out only if the appropriate linker option is enabled.
  • Windows Phone アプリケーションは IL にコンパイルされ、組み込みラインタイムによって実行されます。Windows Phone applications are compiled to IL and executed by the built-in runtime.

さらに、アプリケーションがジェネリックを広く利用する場合、ネイティブにコンパイルされたジェネリックを含む可能性があるため、最終的な実行可能ファイルのサイズはさらに大きくなります。In addition, if an application makes extensive use of generics then the final executable size will further increase since it will contain natively compiled versions of the generic possibilities.

アプリケーションのサイズを容易に縮小できるように、Xamarin プラットフォームにはビルド ツールの一部としてリンカーが含まれています。To help reduce the size of applications, the Xamarin platform includes a linker as part of the build tools. 既定では、リンカーは無効になっているため、アプリケーションのプロジェクト オプションで有効にする必要があります。By default the linker is disabled, and must be enabled in the project options for the application. ビルド時に、アプリケーションで実際に使用される型、およびメンバーを特定するために、アプリケーションのスタティック分析を実行します。At build time, it will perform static analysis of the application to determine which types, and members, are actually used by the application. その後、アプリケーションから未使用の型とメソッドを削除します。It will then remove any unused types and methods from the application.

次のスクリーンショットは、Xamarin.iOS プロジェクト用の Visual Studio for Mac のリンカー オプションを示しています。The following screenshot shows the linker options in Visual Studio for Mac for a Xamarin.iOS project:

Xamarin.iOS のリンカー オプション

次のスクリーンショットは、Xamarin.Android プロジェクト用の Visual Studio for Mac のリンカー オプションを示しています。The following screenshot shows the linker options in Visual Studio for Mac for a Xamarin.Android project:

Xamarin.Android のリンカー オプション

リンカーでは、その動作を制御するための次の 3 つの設定が提供されます。The linker provides three different settings to control its behavior:

  • リンクしない – 未使用の型とメソッドはリンカーによって削除されます。Don’t Link – No unused types and methods will be removed by the linker. パフォーマンス上の理由から、デバッグ ビルドではこれが既定の設定になります。For performance reasons, this is the default setting for debug builds.
  • フレームワーク SDK のみをリンクする/SDK アセンブリのみをリンクする – この設定では、Xamarin に付属しているこれらのアセンブリのサイズのみが縮小されます。Link Framework SDKs/SDK Assemblies Only – This setting will only reduce the size of those assemblies that are shipped by Xamarin. ユーザー コードには影響しません。User code will be unaffected.
  • すべてのアセンブリをリンクする – これは、SDK アセンブリとユーザー コードをターゲットとする、よりアグレッシブな最適化です。Link All Assemblies – This is a more aggressive optimization that will target the SDK assemblies and user code. バインディングの場合、未使用のバッキング フィールドが削除され、各インスタンス (またはバインド オブジェクト) が軽くなるため、メモリ消費量が少なくなります。For bindings this will remove unused backing fields and make each instance (or bound objects) lighter, consuming less memory.

すべてのアセンブリをリンクする は、予期しない方法でアプリケーションが壊れる可能性があるため、注意して使う必要があります。The Link All Assemblies should be used with caution as it may break the application in unexpected ways. リンカーで実行されるスタティック分析では、必要なコードの一部が正しく認識されない場合があり、その結果、コンパイルされたアプリケーションからコードが過剰に削除されることがあります。The static analysis that is performed by the linker may not correctly identify all of the code that is required, resulting in too much code being removed from the compiled application. この状況は、アプリケーションがクラッシュした場合の実行時にしか明らかになりません。This situation will manifest itself only at runtime when the application crashes. そのため、リンカーの動作を変更した後は、アプリケーションを十分テストすることが重要です。Because of this it is important to thoroughly test an application after changing the linker behavior.

テストによって、リンカーがクラスまたはメソッドを正しく削除しなかったことが判明した場合、以下の属性のいずれかを使用して、静的に参照されていないが、アプリケーションには必要な型またはメソッドにマークを付けることができます。If testing does reveal that the linker has incorrectly removed a class or method it is possible to mark types or methods that are not statically referenced but are required by the application by using one of the following attributes:

  • Xamarin.iOS.Foundation.PreserveAttribute – この属性は、Xamarin.iOS プロジェクト用です。Xamarin.iOS.Foundation.PreserveAttribute – This attribute is for Xamarin.iOS projects.
  • Android.Runtime.PreserveAttribute – この属性は、Xamarin.Android プロジェクト用です。Android.Runtime.PreserveAttribute – This attribute is for Xamarin.Android projects.

たとえば、動的にインスタンス化されている型の既定のコンストラクターを保持する必要がある場合があります。For instance, it may be necessary to preserve the default constructors of types that are dynamically instantiated. また、XML シリアル化を使用するには、型のプロパティを維持する必要がある場合があります。Also, the use of XML serialization may require that the properties of types are preserved.

詳細については、iOS のリンカーAndroid のリンカーに関するページを参照してください。For more information, see Linker for iOS and Linker for Android.

その他のサイズ縮小の手法Additional Size Reduction Techniques

モバイル デバイスに電源を供給するさまざまな CPU アーキテクチャがあります。There are a wide variety of CPU architectures that power mobile devices. そのため、Xamarin.iOS および Xamarin.Android では FAT バイナリ が生成されます。これには、各 CPU アーキテクチャのコンパイル済みバージョンのアプリケーションが含まれます。Therefore, Xamarin.iOS and Xamarin.Android produce fat binaries that contain a compiled version of the application for each CPU architecture. これにより、CPU アーキテクチャに関係なく、デバイスでモバイル アプリケーションを確実に実行することができます。This ensures that a mobile application can run on a device regardless of the CPU architecture.

次の手順を使用して、アプリケーションの実行可能ファイルのサイズをさらに縮小することができます。The following steps can be used to further reduce the application executable size:

  • リリース ビルドが生成されていることを確認します。Ensure that a Release build is produced.
  • FAT バイナリが生成されないように、アプリケーションがビルドされるアーキテクチャの数を減らします。Reduce the number of architectures that the application is built for, to avoid a FAT binary being produced.
  • より最適化された実行可能ファイルを生成するために、LLVM コンパイラが使用されていることを確認します。Ensure that the LLVM compiler is being used, to generate a more optimized executable.
  • アプリケーションのマネージド コード サイズを減らします。Reduce the application's managed code size. これは、すべてのアセンブリでリンカーを有効にすることで行うことができます (iOS プロジェクトの場合は [すべてリンク] 、Android プロジェクトの場合は [すべてのアセンブリをリンクする] )。This can be accomplished by enabling the linker on every assembly (Link All for iOS projects, and Link all assemblies for Android projects).

Android アプリは、ABI ("アーキテクチャ") ごとに別の APK に分割することもできます。Android apps can also be split into a separate APK for each ABI ("architecture"). 詳細については、次のブログ記事を参照してください: ご利用の Android アプリのサイズを小さくしておく方法Learn more in this blog post: How To Keep Your Android App Size Down.

イメージ リソースを最適化するOptimize Image Resources

アプリケーションが使用するリソースのうち最もコストが高いものとして画像があります。多くの場合、画像は高解像度でキャプチャされます。Images are some of the most expensive resources that applications use, and are often captured at high resolutions. 画像は細かい部分まで鮮明になりますが、そのような画像を表示するアプリケーションでは通常、画像をデコードするためにより多くの CPU を使用する必要があり、また、デコードされた画像を格納するためにより多くのメモリが必要になります。While this creates vibrant images full of detail, applications that display such images typically require more CPU usage to decode the image and more memory to store the decoded image. 表示サイズを小さくするためにスケールダウンする場合、メモリ内の高解像度画像をデコードするのは無駄です。It is wasteful to decode a high resolution image in memory when it will be scaled down to a smaller size for display. 代わりに、予測された表示サイズに近い、格納された画像の多重解像度バージョンを作成して、CPU 使用量とメモリの占有領域を減らします。Instead, reduce the CPU usage and memory footprint by creating multiple resolution versions of stored images that are close to the predicted display sizes. たとえば、リスト ビューに表示される画像は、全画面で表示される画像よりも解像度が低くなる可能性が最も高くなります。For example, an image displayed in a list view should most likely be a lower resolution than an image displayed at full-screen. さらに、メモリへの影響を最小限に抑えて効率的に画像を表示するために、高解像度の画像のスケールダウン バージョンを読み込むことができます。In addition, scaled down versions of high resolution images can be loaded to efficiently display them with minimal memory impact. 詳細については、「Load Large Bitmaps Efficiently」 (大きなビットマップを効率的に読み込む) を参照してください。For more information, see Load Large Bitmaps Efficiently.

画像の解像度に関係なく、画像リソースを表示すると、アプリのメモリの占有領域が大幅に増える場合があります。Regardless of the image resolution, displaying image resources can greatly increase the app's memory footprint. そのため、必要な場合にのみ作成し、アプリケーションで不要になったらすぐに解放する必要があります。Therefore, they should only be created when required and should be released as soon as the application no longer requires them.

アプリケーションのアクティブ化期間を短くするReduce the Application Activation Period

すべてのアプリケーションに アクティブ化期間 があります。これは、アプリケーションが開始されてから、使用できるようになるまでの期間です。All applications have an activation period, which is the time between when the application is started and when the application is ready to use. このアクティブ化期間に、ユーザーにアプリケーションの第一印象を与えることになります。したがって、ユーザーにアプリケーションに対する好意的な第一印象を与えるためには、アクティブ化期間を減らし、ユーザーの認識を和らげることが重要です。This activation period provides users with their first impression of the application, and so it's important to reduce the activation period and the user's perception of it, in order for them to gain a favorable first impression of the application.

アプリケーションで初期 UI が表示される前に、スプラッシュ スクリーンを提供し、アプリケーションを開始していることをユーザーに示す必要があります。Before an application displays its initial UI, it should provide a splash screen to indicate to the user that the application is starting. アプリケーションで初期 UI をすぐに表示できない場合は、アプリケーションがハングしていないことを確認させるために、スプラッシュ スクリーンを使用して、アクティブ化期間での進行状況をユーザーに知らせる必要があります。If the application can't quickly display its initial UI, the splash screen should be used to inform the user of progress through the activation period, to offer reassurance that the application hasn't hung. この確認は進行状況バー、または同様のコントロールで行うことができます。This reassurance could be a progress bar, or similar control.

アクティブ化期間中に、アプリケーションはアクティブ化ロジックを実行します。これには、多くの場合、リソースの読み込みと処理が含まれます。During the activation period, applications execute activation logic, which often includes the loading and processing of resources. リモートで取得されるのではなく、アプリ内に必要なリソースがパッケージ化されるようにすることで、アクティブ化期間を減らすことができます。The activation period can be reduced by ensuring that required resources are packaged within the app, instead of being retrieved remotely. たとえば、ある状況では、アクティブ化期間中にローカルに格納されたプレースホルダー データを読み込むことが適切な場合があります。For example, in some circumstances it may be appropriate during the activation period to load locally stored placeholder data. その後、初期 UI が表示された時点で、ユーザーはアプリと対話でき、プレースホルダー データをリモート ソースから段階的に置き換えることができます。Then, once the initial UI is displayed, and the user is able to interact with the app, the placeholder data can be progressively replaced from a remote source. さらに、アプリのアクティブ化ロジックでは、ユーザーにアプリケーションの使用を開始させるために必要な作業のみを実行する必要があります。In addition, the app's activation logic should only perform work that's required to let the user start using the application. アセンブリは初回使用時に読み込まれるため、これは追加のアセンブリの読み込みを遅らせる場合に役立ちます。This can help if it delays loading additional assemblies, as assemblies are loaded the first time they are used.

Web サービス通信を減らすReduce Web Service Communication

アプリケーションから Web サービスに接続すると、アプリケーションのパフォーマンスに影響を与える可能性があります。Connecting to a web service from an application can have an impact on application performance. たとえば、ネットワーク帯域幅の使用量が増えると、デバイスのバッテリ使用量が増えます。For example, an increased use of network bandwidth will result in an increased usage of the device's battery. さらに、ユーザーは、帯域幅に制限のある環境でアプリケーションを使用する可能性があります。In addition, users may be using the application in a bandwidth limited environment. したがって、アプリケーションと Web サービス間の帯域幅使用率を制限するのが賢明です。Therefore, it's sensible to limit the bandwidth utilization between an application and a web service.

アプリケーションの帯域幅使用率を減らす 1 つの手法は、ネットワーク経由で転送する前にデータを圧縮することです。One approach to reducing an application's bandwidth utilization is to compress data before transferring it over a network. ただし、圧縮プロセスにより CPU 使用量が増えると、バッテリ使用量も増えます。However, the additional CPU usage from the compression process can also result in an increased battery usage. そのため、ネットワーク経由で圧縮データを移動するかどうかを判断する前に、このトレードオフを慎重に評価する必要があります。Therefore, this tradeoff should be carefully evaluated before deciding whether to move compressed data over a network.

考慮すべきもう 1 つの問題は、アプリケーションと Web サービス間を移動するデータの形式です。Another issue to consider is the format of the data that moves between an application and a web service. 主な 2 つの形式は、拡張マークアップ言語 (XML) と JavaScript Object Notation (JSON) です。The two primary formats are Extensible Markup Language (XML) and JavaScript Object Notation (JSON). XML は、多数の書式設定文字を含むため、比較的大きなデータ ペイロードを生成するテキスト ベースのデータ交換形式です。XML is a text-based data-interchange format that produces relatively large data payloads, because it contains a large number of formatting characters. JSON は、データの送受信時に帯域幅要件を減らせる、コンパクトなデータ ペイロードを生成するテキスト ベースのデータ交換形式です。JSON is a text-based data-interchange format that produces compact data payloads, which results in reduced bandwidth requirements when sending data and receiving data. そのため、多くの場合、モバイル アプリケーションでは JSON 形式が推奨されます。Therefore, JSON is often the preferred format for mobile applications.

アプリケーションと Web サービス間でデータを転送する場合は、データ転送オブジェクト (DTO) を使用することをお勧めします。It's recommended to use data transfer objects (DTOs) when transferring data between an application and a web service. DTO には、ネットワーク経由で転送するためのデータ セットが含まれています。A DTO contains a set of data for transferring across the network. DTO を利用することで、1 つのリモート呼び出しでより多くのデータを送信でき、アプリケーションによるリモート呼び出しの数を減らすことができます。By utilizing DTOs, more data can be transmitted in a single remote call, which can help to reduce the number of remote calls made by the application. 一般に、大きなデータ ペイロードを渡すリモート呼び出しには、小さなデータ ペイロードのみを渡す呼び出しと同様の時間がかかります。Generally, a remote call carrying a larger data payload takes a similar amount of time as a call that only carries a small data payload.

Web サービスから取得されたデータはローカルでキャッシュする必要があります。Web サービスから繰り返し取得されるのではなく、そのキャッシュされたデータが利用されます。Data retrieved from the web service should be cached locally, with the cached data being utilized rather than repeatedly retrieved from the web service. ただし、この手法を採用する場合は、Web サービスで変更されたときにローカル キャッシュのデータを更新するために適切なキャッシュ戦略を実装する必要もあります。However, when adopting this approach a suitable caching strategy should also be implemented to update data in the local cache if it changes in the web service.

まとめSummary

この記事では、Xamarin プラットフォームを使用してビルドされたアプリケーションのパフォーマンスを高めるための手法について説明しました。This article described and discussed techniques for increasing the performance of applications built using the Xamarin platform. これらの手法をすべて使用することで、CPU で実行される作業量や、アプリケーションで消費されるメモリ量を大幅に減らすことができます。Collectively these techniques can greatly reduce the amount of work being performed by a CPU, and the amount of memory consumed by an application.