Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement async API for ODataUtf8JsonWriter #2460

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,6 @@ The easiest way to run the perf benchmarks is to use the [Microsoft.Crank](https
crank --config benchmarks.yml --scenario Components --profile local
```

- Run benchmarks for end-to-end scenarios against a local OData service:

```text
crank --config benchmarks.yml --scenario Service --profile local
```

- Run only ODataReader tests:

```text
Expand All @@ -184,10 +178,10 @@ The easiest way to run the perf benchmarks is to use the [Microsoft.Crank](https
crank --config benchmarks.yml --scenario UriParser --profile local
```

- Run tests that compare serialization performance of ODataWriter and System.Text.Json
- Run tests that compare different writer implementations and configurations

```text
crank --config benchmarks.yml --scenario SerializerBaselines --profile local
crank --config benchmarks.yml --scenario SerializationComparisons --profile local
```

#### Run benchmarks on remote dedicated agents
Expand Down Expand Up @@ -222,15 +216,43 @@ To run benchmarks against the official repo instead of your local repo, pass
the `base=true` variable to the command, e.g.:

```text
crank --config benchmarks.yml --scenario Service --profile local --variable base=true
crank --config benchmarks.yml --scenario ODataWriter --profile local --variable base=true
```

This will cause the crank agent to clone the official repo and run the tests against the `master` branch.

You can specify a different branch, commit or tag using the `baseBranch` variable:

```text
crank --config benchmarks.yml --scenario Service --profile local --variable base=true --variable baseBranch=v7.6.4
crank --config benchmarks.yml --scenario ODataWriter --profile local --variable base=true --variable baseBranch=v7.6.4
```

#### Run load tests

Besides benchmarks, we also have some load tests which measure request round-trips from a client to a server.
These can be used to evaluate how OData libraries behave when handling multiple concurrent requests on the same server.

We have tests that evaluate different writer implementations, serializing a simple a static collection response
on each request:

```text
crank --config loadtests.yml --config lab-windows --scenario SerializationComparisons --application.options.counterProviders System.Runtime --variable writer=ODataMessageWriter
```

This scenario allows you to choose which writer implementation is used to process the response as well. It also allows yout to configure different aspects of the requests, e.g. number of connections, max request per second, etc.

For more information about these tests, [read this doc](test/PerformanceTests/SerializationComparisonsTests/README.md).

#### Collecting traces

Crank can collect and download native trace files from the benchmarked application that you can analyze in specialized tools using the `--[job].collect true` option, where `[job]` is the name of a job defined in the `.yml` config file.

On Windows, `--[job].collect true` option will collect traces using [PerfView](https://github.com/Microsoft/perfview) download an `.etl` trace file that you can you can also analyze using PerfView.

For example, the `loadtest.yml` config defines an `application` job which refers to the server handling the requests. We can collect traces from the server as follows:

```
crank --config loadtests.yml --config lab-windows --scenario SerializationComparisons --variable writer=ODataMessageWriter --application.collect true
```

#### Comparing benchmarks
Expand Down
11 changes: 5 additions & 6 deletions benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ jobs:
benchmarkDotNet: true
onConfigure:
- "{{setSourceScript}}"
serializationBaselines:
serializationComparisons:
source:
project: test/PerformanceTests/SerializationBaselineTests/SerializationBaselineTests.csproj
project: test/PerformanceTests/SerializationComparisonsTests/JsonWriterBenchmarks/JsonWriterBenchmarks.csproj
variables:
filter: "*"
jobType: default
Expand All @@ -58,7 +58,6 @@ jobs:
benchmarkDotNet: true
onConfigure:
- "{{setSourceScript}}"

scenarios:
Reader:
application:
Expand All @@ -85,11 +84,11 @@ scenarios:
job: service
variables:
filter: "*"
SerializerBaselines:
SerializationComparisons:
application:
job: serializationBaselines
job: serializationComparisons
variables:
filter: "*"
filter: "*File*"

profiles:
local:
Expand Down
74 changes: 74 additions & 0 deletions loadtests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
imports:
- https://raw.githubusercontent.com/dotnet/crank/main/src/Microsoft.Crank.Jobs.Bombardier/bombardier.yml

jobs:
server:
source:
localFolder: .
project: test/PerformanceTests/SerializationComparisonsTests/TestServer/TestServer.csproj
readyStateText: Application started.
variables:
port: 5000
host: "*"
arguments: '--urls=http://{{host}}:{{port}}'


scenarios:
SerializationComparisons:
application:
job: server
load:
job: bombardier
variables:
serverPort: 5000
connections: 128
duration: 30
count: 50
writer: 'ODataMessageWriter'
path: /customers/{{writer}}?count={{count}}

profiles:
local:
variables:
serverPort: 5000
serverAddress: localhost
host: localhost
jobs:
application:
endpoints:
- http://localhost:5010
variables:
host: localhost
load:
endpoints:
- http://localhost:5010
lab-windows:
variables:
serverPort: 5000
serverAddress: 10.0.0.110
cores: 8
jobs:
application:
endpoints:
- http://asp-perf-win:5001
variables:
serverAddress: 10.0.0.110
load:
endpoints:
- http://asp-perf-db:5001
variables:
serverAddress: 10.0.0.110
remote-windows:
jobs:
application:
endpoints:
- http://enter-remote-server-address:5010
load:
endpoints:
- http://localhost:5010
variables:
serverAddress: enter-remote-server-address
serverPort: 5000
numConnections: 128


Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,21 @@ public IJsonWriter CreateJsonWriter(Stream stream, bool isIeee754Compatible, Enc

return new ODataUtf8JsonWriter(stream, isIeee754Compatible, encoding, encoder: this.encoder);
}

public IJsonWriterAsync CreateAsynchronousJsonWriter(Stream stream, bool isIeee754Compatible, Encoding encoding)
{
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}

if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}

return new ODataUtf8JsonWriter(stream, isIeee754Compatible, encoding, encoder: this.encoder);
}
}
}
#endif
13 changes: 12 additions & 1 deletion src/Microsoft.OData.Core/Json/IStreamBasedJsonWriterFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
namespace Microsoft.OData.Json
{
/// <summary>
/// The interface of factories that create <see cref="IJsonWriter"/> instances
/// The interface of factories that create <see cref="IJsonWriterAsync"/> and <see cref="IJsonWriter"/> instances
/// that write directly to a <see cref="Stream"/> rather than a <see cref="TextWriter"/>.
/// </summary>
[CLSCompliant(false)]
Expand All @@ -25,5 +25,16 @@ public interface IStreamBasedJsonWriterFactory
/// <param name="encoding">The text encoding of the output data.</param>
/// <returns>The JSON writer created.</returns>
IJsonWriter CreateJsonWriter(Stream stream, bool isIeee754Compatible, Encoding encoding);

/// <summary>
/// Creates an asynchronous JSON writer of <see cref="IJsonWriterAsync"/>.
/// The returned instance should also implement the synchronous <see cref="IJsonWriter"/>
/// interface.
/// </summary>
/// <param name="stream">Output stream to which the resulting <see cref="IJsonWriterAsync"/> should write data.</param>
/// <param name="isIeee754Compatible">True if it is IEEE754Compatible.</param>
/// <param name="encoding">The text encoding of the output data.</param>
/// <returns>The JSON writer created.</returns>
IJsonWriterAsync CreateAsynchronousJsonWriter(Stream stream, bool isIeee754Compatible, Encoding encoding);
}
}
Loading