The Google Ads .NET client library simplifies your app's interactions with the
Google Ads API, with minimal configuration on your part. However, the overall
performance depends heavily on how the library is used and integrated with your
app.

This guide covers performance optimizations that are specific to .NET apps, and
complements the [best practices](https://developers.google.com/google-ads/api/docs/best-practices/overview) that are
generally applicable to the Google Ads API.

## Reuse GoogleAdsClient whenever possible

`GoogleAdsClient` represents a user's session when making API calls. It provides
optimizations such as:

- Caching the [gRPC channels](https://grpc.io/docs/what-is-grpc/core-concepts/#channels) used by API services. This reduces the setup time when making initial API calls.
- Reusing access tokens when possible. This reduces the number of round trips that the Google Ads .NET client library has to perform to refresh access tokens.

## Use access tokens from a manager level account when possible

If you have an access token issued at a manager account level, you can use it to
make API calls against all Google Ads client accounts under that account hierarchy.
When combined with reusing `GoogleAdsClient` instances, this can further reduce
the number of round trips the client library has to perform to refresh access
tokens.

## Use SearchStream instead of Search whenever possible

The Google Ads API provides two main ways to retrieve objects:
[`GoogleAdsService.Search`](https://developers.google.com/google-ads/api/reference/rpc/v26/GoogleAdsService/Search) (which uses
pagination) and
[`GoogleAdsService.SearchStream`](https://developers.google.com/google-ads/api/reference/rpc/v26/GoogleAdsService/SearchStream)
(which uses streaming).

While `Search` sends multiple paginated requests to download an entire report,
`SearchStream` sends a single request and initiates a persistent connection with
the Google Ads API regardless of the report size. By eliminating the round-trip network
time required to request each individual page of a `Search` response,
`SearchStream` generally offers improved performance over paging. See the
[streaming reports guide](https://developers.google.com/google-ads/api/docs/reporting/streaming) to learn more about when
to choose each method.

## Manually manage access token refreshes

In certain stateless environments such as [Google Cloud Functions](https://cloud.google.com/functions), it
might not be feasible to reuse `GoogleAdsClient` instances across invocations.
Such environments have their own best practices to persist and reuse data.

In `Google.Ads.GoogleAds` **`v27.0.0` and later** , you can inject your own
pre-configured `ICredential` instance directly on `GoogleAdsConfig` using the
`Credentials` property and disable channel caching (`UseChannelCache = false`).

If you prefer to encapsulate credential creation in a custom configuration
class (or are using an earlier version of the library), you can extend the
`GoogleAdsConfig` class to perform your own access token refreshes as follows:

    // Create your own config class by extending the GoogleAdsConfig class.
    class MyGoogleAdsConfig : GoogleAdsConfig
    {
        public MyGoogleAdsConfig() : base()
        {
            // Disable the library's built-in channel caching mechanism.
            UseChannelCache = false;
        }

        protected override ICredential CreateCredentials()
        {
            // Create your own ICredential object here. You may refer to the
            // default implementation of GoogleAdsConfig.CreateCredentials
            // for an example.
        }
    }

    // Use your own config class when initializing the GoogleAdsClient instance.
    MyGoogleAdsConfig myConfig = new MyGoogleAdsConfig();
    GoogleAdsClient client = new GoogleAdsClient(myConfig);

## Compile for release build

Make sure you compile your app using the [Release configuration](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-build#examples)
when deploying to the server. When using the Debug configuration, your app
compiles with full symbolic debug information and without compiler
optimizations.

## Profile your app

Profile your app both for CPU and memory usage to identify performance
bottlenecks. Visual Studio provides [Diagnostic tools](https://learn.microsoft.com/en-us/visualstudio/profiling/) to help
profile your app. There are also other
[commercial profiling tools](https://learn.microsoft.com/en-us/aspnet/core/performance/diagnostic-tools#other-third-party-performance-tools) that are available.

## Use async methods

[Asynchronous programming using the async-await paradigm](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/) helps
avoid performance bottlenecks and enhances the overall responsiveness of your
app. The Google Ads .NET library generates async methods for all services and RPC
methods.

### Async methods cancellation

You can use the `callSettings` parameter to pass a
[`CancellationToken`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) to async methods such as
`SearchStreamAsync`:

    using CancellationTokenSource cancellationTokenSource =
        new CancellationTokenSource();
    cancellationTokenSource.CancelAfter(3000);
    CallSettings callSettings =
        CallSettings.FromCancellationToken(cancellationTokenSource.Token);

    string query = "SELECT campaign.name FROM campaign";
    var request = new SearchGoogleAdsStreamRequest()
    {
        CustomerId = customerId.ToString(),
        Query = query,
    };

    GoogleAdsServiceClient googleAdsService = client.GetService(
        Services.V26.GoogleAdsService);

    await googleAdsService.SearchStreamAsync(
        request,
        (SearchGoogleAdsStreamResponse resp) =>
        {
            foreach (GoogleAdsRow googleAdsRow in resp.Results)
            {
                // Process the row.
            }
        },
        callSettings);

## Turn off logging when you can

The Google Ads .NET library turns off logging by default and uses a lazy logging
approach that gives your app better performance. If you turn on logging during
development, make sure you turn it off in the production environment. If you
need to monitor for specific failing requests in production, you can do one or
more of the following steps without adversely affecting your app's performance:

- Turn on only the summary logs.
- Set the full logs to the `ERROR` level.
- Save the request ID for specific failing requests so you can share it with support channels.

See the [logging guide](https://developers.google.com/google-ads/api/docs/client-libs/dotnet/logging) to learn more.

## Use the ReadyToRun option

Modern .NET supports precompiling your binaries to a specific platform and
architecture by setting `PublishReadyToRun` to `true`, and then publishing the
binary by specifying a valid `RuntimeIdentifier`. See the
[`ReadyToRun` deployment guide](https://learn.microsoft.com/en-us/dotnet/core/deploying/ready-to-run) to learn more.

## Use TieredCompilation

`TieredCompilation` (enabled by default in modern .NET versions such as .NET 8)
allows .NET to identify hotspots and improve runtime performance. Tiered
compilation works well with `ReadyToRun` because it can use the pre-generated
image for fast startup and then recompile hot methods with full optimizations.
See the [`TieredCompilation` guide](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0#tiered-compilation) to learn more.

## Fine-tune your garbage collection (GC)

.NET provides two general profiles for garbage collection (GC): a workstation
profile and a server profile. These two profiles have
[differing performance trade-offs](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/workstation-server-gc). Dedicated server apps
using the Google Ads .NET library often perform better when running in a server
profile.

> [!CAUTION]
> **Caution:** Enabling `ServerGarbageCollection` or `RetainVMGarbageCollection` in resource-constrained environments (such as small Docker containers or serverless functions) can significantly increase memory consumption and cause out-of-memory errors. Benchmark your application's memory footprint before enabling these settings in containerized environments.

You can benefit from fine-tuning the following GC settings:

- **Server garbage collection:** Server garbage collection allows the .NET
  runtime to give higher throughput to a Google Ads API app by operating on multiple
  GC heaps and threads. See the [server GC guide](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/performance#workstation-or-server-garbage-collection) for more
  details. You can turn on server garbage collection by adding the following
  lines to your app's `.csproj` file:

      <PropertyGroup>
        <ServerGarbageCollection>true</ServerGarbageCollection>
      </PropertyGroup>

- **Concurrent garbage collection:** You can turn on
  [concurrent garbage collection](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/background-gc#concurrent-garbage-collection) to give the .NET GC a
  dedicated thread for garbage collection in generation 2. This setting can be
  useful when processing large reports. You can turn on concurrent garbage
  collection by adding the following lines to your app's `.csproj` file:

      <PropertyGroup>
        <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
      </PropertyGroup>

- **Retain VM garbage collection:** The
  [`RetainVMGarbageCollection` setting](https://learn.microsoft.com/en-us/dotnet/core/run-time-config/garbage-collector#retain-vm) configures whether segments
  of virtual memory that should be deleted are put on a standby list for
  future use, or are released back to the operating system (OS). You can turn
  on virtual memory retention by adding the following lines to your app's
  `.csproj` file:

      <PropertyGroup>
        <RetainVMGarbageCollection>true</RetainVMGarbageCollection>
      </PropertyGroup>

You can fine-tune your GC by choosing a setup that balances workstation and
server behavior. All [the relevant GC settings](https://learn.microsoft.com/en-us/dotnet/core/run-time-config/garbage-collector#manage-resource-usage) can be specified in
your .NET app's `runtimeconfig.json` file, through environment variables, or in
your `App.config`.