Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Optimize FindFirstCharToEncode for JavaScriptEncoder.Default and Relaxed using Sse2 intrinsics.#41933

Merged
ahsonkhan merged 9 commits intodotnet:masterfrom
ahsonkhan:OptimizeEscapingSTEW
Oct 23, 2019
Merged

Optimize FindFirstCharToEncode for JavaScriptEncoder.Default and Relaxed using Sse2 intrinsics.#41933
ahsonkhan merged 9 commits intodotnet:masterfrom
ahsonkhan:OptimizeEscapingSTEW

Conversation

@ahsonkhan
Copy link

@ahsonkhan ahsonkhan commented Oct 21, 2019

Follow-up to #41845 to optimize the built-in JavascriptEncoder using a similar approach.

Also added some tests to improve code coverage.

  • Escaping UTF-16 (for strings of length >= 16)
    • using Default improved 2x
    • using Relaxed improved 3x
    • using Custom improved 15-20%
    • small regression for lengths <= 4.
  • Escaping UTF-8 (for strings of length >= 16)
    • using Default improved 2-5x
    • using Relaxed improved 6-10x
    • using Custom improved 2-3x
    • small regression for lengths <= 2.
public unsafe class NeedsEscapingTest
{
    [Params(E.Default, E.Relaxed, E.Custom)]
    public E Encoder { get; set; }

    [Params(1, 2, 4, 7, 8, 15, 16, 17, 31, 32, 33, 47, 100, 1000)]
    public int DataLength { get; set; }

    public enum E
    {
        Default,
        Relaxed,
        Custom
    }

    private string _source;
    private byte[] _sourceUtf8;
    private JavaScriptEncoder _encoder;

    [GlobalSetup]
    public void Setup()
    {
        var random = new Random(42);

        var array = new char[DataLength];
        for (int i = 0; i < DataLength; i++)
        {
            array[i] = (char)random.Next(97, 123);
        }
        _source = new string(array);
        _sourceUtf8 = Encoding.UTF8.GetBytes(_source);

        _encoder = null;
        switch (Encoder)
        {
            case E.Default:
                _encoder = JavaScriptEncoder.Default;
                break;
            case E.Relaxed:
                _encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
                break;
            case E.Custom:
                _encoder = JavaScriptEncoder.Create(new TextEncoderSettings(UnicodeRanges.All));
                break;
        }
        _encoder.FindFirstCharacterToEncodeUtf8(_sourceUtf8);
    }

    [BenchmarkCategory(Categories.CoreFX, Categories.JSON)]
    [Benchmark (Baseline = true)]
    public int NeedsEscapingUtf16()
    {
        fixed (char* ptr = _source)
        {
            return _encoder.FindFirstCharacterToEncode(ptr, _source.Length);
        }
    }

    [BenchmarkCategory(Categories.CoreFX, Categories.JSON)]
    [Benchmark]
    public int NeedsEscapingUtf8()
    {
        return _encoder.FindFirstCharacterToEncodeUtf8(_sourceUtf8);
    }
}
Method Encoder DataLength Before (ns) After (ns) Ratio
NeedsEscapingUtf16 Default 1 2.845 5.359 1.884
NeedsEscapingUtf8 Default 1 5.286 5.32 1.006
NeedsEscapingUtf16 Default 2 3.836 5.651 1.473
NeedsEscapingUtf8 Default 2 6.866 6.117 0.891
NeedsEscapingUtf16 Default 4 5.96 7.406 1.243
NeedsEscapingUtf8 Default 4 11.188 7.489 0.669
NeedsEscapingUtf16 Default 7 9.746 10.047 1.031
NeedsEscapingUtf8 Default 7 18.271 9.449 0.517
NeedsEscapingUtf16 Default 8 10.732 8.708 0.811
NeedsEscapingUtf8 Default 8 19.119 9.904 0.518
NeedsEscapingUtf16 Default 15 18.068 14.783 0.818
NeedsEscapingUtf8 Default 15 35.961 14.552 0.405
NeedsEscapingUtf16 Default 16 19.266 10.416 0.541
NeedsEscapingUtf8 Default 16 34.358 9.519 0.277
NeedsEscapingUtf16 Default 17 20.162 11.761 0.583
NeedsEscapingUtf8 Default 17 38.229 9.16 0.240
NeedsEscapingUtf16 Default 31 35.119 22.04 0.628
NeedsEscapingUtf8 Default 31 70.864 18.698 0.264
NeedsEscapingUtf16 Default 32 37.013 18.846 0.509
NeedsEscapingUtf8 Default 32 80.238 10.797 0.135
NeedsEscapingUtf16 Default 33 41.737 20.024 0.480
NeedsEscapingUtf8 Default 33 74.927 11.542 0.154
NeedsEscapingUtf16 Default 47 53.155 31.701 0.596
NeedsEscapingUtf8 Default 47 102.116 20.617 0.202
NeedsEscapingUtf16 Default 100 118.166 57.782 0.489
NeedsEscapingUtf8 Default 100 208.161 23.387 0.112
NeedsEscapingUtf16 Default 1000 1,068.02 537.261 0.503
NeedsEscapingUtf8 Default 1000 2,077.12 129.447 0.062
           
NeedsEscapingUtf16 Relaxed 1 3.523 4.45 1.263
NeedsEscapingUtf8 Relaxed 1 5.164 5.382 1.042
NeedsEscapingUtf16 Relaxed 2 6.617 5.444 0.823
NeedsEscapingUtf8 Relaxed 2 9.37 6.744 0.720
NeedsEscapingUtf16 Relaxed 4 7.057 7.594 1.076
NeedsEscapingUtf8 Relaxed 4 13.711 9.583 0.699
NeedsEscapingUtf16 Relaxed 7 9.742 10.223 1.049
NeedsEscapingUtf8 Relaxed 7 20.175 13.549 0.672
NeedsEscapingUtf16 Relaxed 8 10.666 5.822 0.546
NeedsEscapingUtf8 Relaxed 8 20.463 15.222 0.744
NeedsEscapingUtf16 Relaxed 15 18.347 13.148 0.717
NeedsEscapingUtf8 Relaxed 15 36.851 24.855 0.674
NeedsEscapingUtf16 Relaxed 16 19.439 7.579 0.390
NeedsEscapingUtf8 Relaxed 16 39.43 6.166 0.156
NeedsEscapingUtf16 Relaxed 17 19.919 8.377 0.421
NeedsEscapingUtf8 Relaxed 17 42.782 7.535 0.176
NeedsEscapingUtf16 Relaxed 31 34.941 18.058 0.517
NeedsEscapingUtf8 Relaxed 31 78.846 27.82 0.353
NeedsEscapingUtf16 Relaxed 32 36.679 13.355 0.364
NeedsEscapingUtf8 Relaxed 32 80.051 8.601 0.107
NeedsEscapingUtf16 Relaxed 33 38.289 14.08 0.368
NeedsEscapingUtf8 Relaxed 33 83.212 10.119 0.122
NeedsEscapingUtf16 Relaxed 47 52.697 23.491 0.446
NeedsEscapingUtf8 Relaxed 47 112.786 33.978 0.301
NeedsEscapingUtf16 Relaxed 100 119.245 41.575 0.349
NeedsEscapingUtf8 Relaxed 100 235.103 21.904 0.093
NeedsEscapingUtf16 Relaxed 1000 1,083.64 360.613 0.333
NeedsEscapingUtf8 Relaxed 1000 2,306.57 142.98 0.062
           
NeedsEscapingUtf16 Custom 1 2.794 3.789 1.356
NeedsEscapingUtf8 Custom 1 4.726 5.637 1.193
NeedsEscapingUtf16 Custom 2 3.679 4.455 1.211
NeedsEscapingUtf8 Custom 2 7.586 6.657 0.878
NeedsEscapingUtf16 Custom 4 5.993 6.336 1.057
NeedsEscapingUtf8 Custom 4 12.065 8.793 0.729
NeedsEscapingUtf16 Custom 7 9.794 9.196 0.939
NeedsEscapingUtf8 Custom 7 20.017 12.328 0.616
NeedsEscapingUtf16 Custom 8 10.898 9.973 0.915
NeedsEscapingUtf8 Custom 8 21.423 13.255 0.619
NeedsEscapingUtf16 Custom 15 18.924 16.201 0.856
NeedsEscapingUtf8 Custom 15 37.769 21.461 0.568
NeedsEscapingUtf16 Custom 16 21.059 17.102 0.812
NeedsEscapingUtf8 Custom 16 39.721 17.138 0.431
NeedsEscapingUtf16 Custom 17 21.333 17.613 0.826
NeedsEscapingUtf8 Custom 17 42.232 18.459 0.437
NeedsEscapingUtf16 Custom 31 35.81 30.757 0.859
NeedsEscapingUtf8 Custom 31 77.807 37.533 0.482
NeedsEscapingUtf16 Custom 32 37.572 31.069 0.827
NeedsEscapingUtf8 Custom 32 81.055 28.39 0.350
NeedsEscapingUtf16 Custom 33 37.351 30.971 0.829
NeedsEscapingUtf8 Custom 33 85.248 30.07 0.353
NeedsEscapingUtf16 Custom 47 54.932 43.93 0.800
NeedsEscapingUtf8 Custom 47 115.287 48.567 0.421
NeedsEscapingUtf16 Custom 100 118.678 91.347 0.770
NeedsEscapingUtf8 Custom 100 237.319 79.042 0.333
NeedsEscapingUtf16 Custom 1000 1,066.93 882.03 0.827
NeedsEscapingUtf8 Custom 1000 2,288.41 722.101 0.316

Next Steps:

  1. Evaluate if we can get rid of the special-casing in S.T.Json

cc @GrabYourPitchforks, @gfoidl, @tarekgh, @tannergooding, @ViktorHofer, @steveharter

@ahsonkhan ahsonkhan added this to the 5.0 milestone Oct 21, 2019
<RootNamespace>System.Text.Encodings.Web</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Configurations>netstandard-Debug;netstandard-Release;netstandard2.1-Debug;netstandard2.1-Release</Configurations>
<Configurations>netcoreapp-Debug;netcoreapp-Release;netstandard-Debug;netstandard-Release;netstandard2.1-Debug;netstandard2.1-Release</Configurations>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a new netcoreapp config to the source implementation (to make use of intrinsics which are only available on core).

cc @ericstj

Copy link
Member

@ericstj ericstj Oct 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So adding a config here is OK, so long as its in packages. One thing you need to be careful about is adding a configuration where an app might rollforward with a newer package and downgrade the inbox implementation with lesser one.

Consider: App targets netcoreapp3.0, references a new package, say from 5.0 wave. App rolls forward to 3.1. 5.0 binary might have higher version than 3.1 inbox, but be missing your feature. One way to mitigate this is to give the netcoreapp build a higher version. Another way to mitigate is to make sure you never target a TFM that would be the target of a rollforward (EG: target netcoreapp3.0 instead of netcoreapp3.1).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressing this in #42069

// The worst case encoding is 6 output chars per input char: [input] U+FFFF -> [output] "\uFFFF"
// We don't need to worry about astral code points since they're represented as encoded
// surrogate pairs in the output.
public override int MaxOutputCharactersPerInputCharacter => 12; // "\uFFFF\uFFFF" is the longest encoded form
Copy link
Member

@gfoidl gfoidl Oct 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code below is duplicated from

internal sealed class DefaultJavaScriptEncoder : JavaScriptEncoder
(up to L315 in this file).

Derive this class from DefaultJavaScriptEncoder, so that the code can be reused (from the base-class)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered that, but DefaultJavaScriptEncoder is a sealed class and hence we can't derive from it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing sealed might not be an option (perf-wise), so maybe move the "shared" code to a helper and use from both places?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the shared code into a common static helper class. TryEncodeUnicodeScalar cannot be moved fully since it calls the virtual WillEncode which is different and extracting out the common snippets outside of that within TryEncodeUnicodeScalar doesn't seem worth it (especially since UnsafeRelaxedJavaScriptEncoder.TryEncodeUnicodeScalar has difference there too).

int processNextSixteen = idx + 16;
Debug.Assert(processNextSixteen <= utf8Text.Length);
if (index != 0)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this block into a separate method, to keep the scan-loop tight?

Copy link
Author

@ahsonkhan ahsonkhan Oct 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving this out to another method slows the "all-ascii" code path quite a bit (~2x).

Copy link
Author

@ahsonkhan ahsonkhan Oct 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The culprit here was passing idx by ref.

It was causing a bunch of stack<->register moves:

00007FFD76D8B0DA  mov         ecx,dword ptr [rsp+48h]  
00007FFD76D8B0DE  inc         ecx  
00007FFD76D8B0E0  mov         dword ptr [rsp+48h],ecx  

Comment on lines +733 to +758
while (idx < processNextSixteen)
{
Debug.Assert((ptr + idx) <= (ptr + utf8Text.Length));

if (UnicodeUtility.IsAsciiCodePoint(ptr[idx]))
{
if (DoesAsciiNeedEncoding(ptr[idx]) == 1)
{
goto Return;
}
idx++;
}
else
{
OperationStatus opStatus = UnicodeHelpers.DecodeScalarValueFromUtf8(utf8Text.Slice(idx), out uint nextScalarValue, out int utf8BytesConsumedForScalar);

Debug.Assert(nextScalarValue <= int.MaxValue);
if (opStatus != OperationStatus.Done || WillEncode((int)nextScalarValue))
{
goto Return;
}

Debug.Assert(opStatus == OperationStatus.Done);
idx += utf8BytesConsumedForScalar;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same code is used in the sequential path. Refactor to a method?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have in-line gotos here making it hard to refactor out into a method. Let me try and see if perf is impacted.

Copy link
Author

@ahsonkhan ahsonkhan Oct 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a small regression when I do this (~10%, goes from 12.5 ns to 14 ns, dealing with 7 characters).

Here's basically what I changed:

while (idx < utf8Text.Length)
{
    if (NeedsEscapingHelper(ptr, ref idx, utf8Text))
    {
        goto Return;
    }
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe bool NeedsEscapingHelper(byte* ptr, ref int idx, ReadOnlySpan<byte> utf8Text)
{
    Debug.Assert((ptr + idx) <= (ptr + utf8Text.Length));

    bool result = true;

    if (UnicodeUtility.IsAsciiCodePoint(ptr[idx]))
    {
        if (DoesAsciiNeedEncoding(ptr[idx]) == 1)
        {
            goto Return;
        }
        idx++;
    }
    else
    {
        OperationStatus opStatus = UnicodeHelpers.DecodeScalarValueFromUtf8(utf8Text.Slice(idx), out uint nextScalarValue, out int utf8BytesConsumedForScalar);

        Debug.Assert(nextScalarValue <= int.MaxValue);
        if (opStatus != OperationStatus.Done || WillEncode((int)nextScalarValue))
        {
            goto Return;
        }

        Debug.Assert(opStatus == OperationStatus.Done);
        idx += utf8BytesConsumedForScalar;
    }

    result = false;

Return:
    return result;
}

I want to avoid the code duplication, but for now, leaning towards leaving it as is.

The disassembly grows as well from 99 instructions to 114 (if we just look at the remainder processing while loop).
sharplab.io

if (needsEscaping == 0)
{
needsEscaping = WillEncode(value) ? 1 : -1;
_asciiNeedsEscaping[value] = needsEscaping;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DoesAsciiNeedEncoding is used quite often, so this method should be as tiny as possible.
Therefore move this lazy assignement of the values to the ctor (WillEncode is already known there)?
Then this method is just the array-lookup.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which ctor? TextEncoder is an abstract class.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which ctor?

Currently there is none, but you can create a protected ctor?

Copy link
Author

@ahsonkhan ahsonkhan Oct 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The types that implement TextEncoder are also what implement WillEncode. If we added a protected ctor to TextEncoder it will end up calling WillEncode on the concrete types before the concrete type's ctor finishes.

So:
DefaultJavaScriptEncoder ctor starts -> Calls base TextEncoder ctor -> Calls DefaultJavaScriptEncoder.WillEncode -> This fails because the DefaultJavaScriptEncoder Ctor isn't complete yet (including setting up _allowedCharacters).

We could, theoretically, copy/paste and override the base-types FindFirstCharacterToEncodeUtf8 for DefaultJavaScriptEncoder and duplicate that logic. For that concrete type, we could optimize the DoesAsciiNeedEncoding and init the array up-front.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or call an initialize-method (outside of the loop which uses DoesAsciiNeedEncoding) that ensures the array is populated with data?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the ~80% case has been addressed already (with the built-in encoders). I am less inclined to optimize TextEncoder itself any further since most people wouldn't be calling those methods anyway (at least when it comes to S.T.Json).

We can do that separately, for 5.0/master (I'd like to port this PR to 3.1). Looks like we can get ~15% perf win from doing so.

private bool _asciiCacheNeedsInit = true;

public virtual unsafe int FindFirstCharacterToEncodeUtf8(ReadOnlySpan<byte> utf8Text)
{
    if (_asciiCacheNeedsInit)
    {
        InitializeAsciiCache();
    }
    // ... rest of the current impl
}

[MethodImpl(MethodImplOptions.NoInlining)]
private void InitializeAsciiCache()
{
    Debug.Assert(_asciiCacheNeedsInit);

    for (int i = 0; i < _asciiNeedsEscaping.Length; i++)
    {
        _asciiNeedsEscaping[i] = WillEncode(i) ? 1 : -1;
    }
    _asciiCacheNeedsInit = false;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I touched this in #42073 also.

@ahsonkhan
Copy link
Author

Unrelated infra failure (corefx-ci (Windows Build x64_Debug)):
https://dev.azure.com/dnceng/public/_build/results?buildId=396606

Sending Job to Windows.10.Amd64.Client19H1.ES.Open...

D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error : StorageException: An error occurred while sending the request. [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteAsyncInternal[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext, CancellationToken token) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob.<>c__DisplayClass28_0.<<UploadFromStreamAsyncHelper>b__0>d.MoveNext() [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error : --- End of stack trace from previous location where exception was thrown --- [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at Microsoft.DotNet.Helix.Client.ContainerBase.UploadFileAsync(Stream stream, String blobName) in /_/src/Microsoft.DotNet.Helix/JobSender/StorageHelpers/ContainerBase.cs:line 24 [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at Microsoft.DotNet.Helix.Client.DirectoryPayload.DoUploadAsync(IBlobContainer payloadContainer, Action`1 log) in /_/src/Microsoft.DotNet.Helix/JobSender/Payloads/DirectoryPayload.cs:line 100 [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at Microsoft.DotNet.Helix.Client.DirectoryPayload.UploadAsync(IBlobContainer payloadContainer, Action`1 log) in /_/src/Microsoft.DotNet.Helix/JobSender/Payloads/DirectoryPayload.cs:line 55 [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at Microsoft.DotNet.Helix.Client.JobDefinition.<>c__DisplayClass74_0.<<SendAsync>b__0>d.MoveNext() in /_/src/Microsoft.DotNet.Helix/JobSender/JobDefinition.cs:line 161 [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error : --- End of stack trace from previous location where exception was thrown --- [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at Microsoft.DotNet.Helix.Client.JobDefinition.SendAsync(Action`1 log) in /_/src/Microsoft.DotNet.Helix/JobSender/JobDefinition.cs:line 160 [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at Microsoft.DotNet.Helix.Sdk.SendHelixJob.ExecuteCore(CancellationToken cancellationToken) in /_/src/Microsoft.DotNet.Helix/Sdk/SendHelixJob.cs:line 210 [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at Microsoft.DotNet.Helix.Sdk.HelixTask.Execute() in /_/src/Microsoft.DotNet.Helix/Sdk/HelixTask.cs:line 53 [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error : HttpRequestException: An error occurred while sending the request. [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at System.Net.Http.HttpConnectionPool.SendWithNtConnectionAuthAsync(HttpConnection connection, HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteAsyncInternal[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext, CancellationToken token) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error : IOException: Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.. [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at System.Net.Security.SslStream.<FillBufferAsync>g__InternalFillBufferAsync|215_0[TReadAdapter](TReadAdapter adap, ValueTask`1 task, Int32 min, Int32 initial) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at System.Net.Security.SslStream.ReadAsyncInternal[TReadAdapter](TReadAdapter adapter, Memory`1 buffer) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :    at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error : SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :  [D:\a\1\s\eng\sendtohelix.proj]
D:\a\1\s\.packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error :  [D:\a\1\s\eng\sendtohelix.proj]
##[error].packages\microsoft.dotnet.helix.sdk\5.0.0-beta.19518.2\tools\Microsoft.DotNet.Helix.Sdk.MonoQueue.targets(47,5): error : (NETCORE_ENGINEERING_TELEMETRY=Test) StorageException: An error occurred while sending the request.
   at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteAsyncInternal[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext, CancellationToken token)
   at Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob.<>c__DisplayClass28_0.<<UploadFromStreamAsyncHelper>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.DotNet.Helix.Client.ContainerBase.UploadFileAsync(Stream stream, String blobName) in /_/src/Microsoft.DotNet.Helix/JobSender/StorageHelpers/ContainerBase.cs:line 24
   at Microsoft.DotNet.Helix.Client.DirectoryPayload.DoUploadAsync(IBlobContainer payloadContainer, Action`1 log) in /_/src/Microsoft.DotNet.Helix/JobSender/Payloads/DirectoryPayload.cs:line 100
   at Microsoft.DotNet.Helix.Client.DirectoryPayload.UploadAsync(IBlobContainer payloadContainer, Action`1 log) in /_/src/Microsoft.DotNet.Helix/JobSender/Payloads/DirectoryPayload.cs:line 55
   at Microsoft.DotNet.Helix.Client.JobDefinition.<>c__DisplayClass74_0.<<SendAsync>b__0>d.MoveNext() in /_/src/Microsoft.DotNet.Helix/JobSender/JobDefinition.cs:line 161
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.DotNet.Helix.Client.JobDefinition.SendAsync(Action`1 log) in /_/src/Microsoft.DotNet.Helix/JobSender/JobDefinition.cs:line 160
   at Microsoft.DotNet.Helix.Sdk.SendHelixJob.ExecuteCore(CancellationToken cancellationToken) in /_/src/Microsoft.DotNet.Helix/Sdk/SendHelixJob.cs:line 210
   at Microsoft.DotNet.Helix.Sdk.HelixTask.Execute() in /_/src/Microsoft.DotNet.Helix/Sdk/HelixTask.cs:line 53
HttpRequestException: An error occurred while sending the request.
   at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithNtConnectionAuthAsync(HttpConnection connection, HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
   at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteAsyncInternal[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext, CancellationToken token)
IOException: Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond..
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token)
   at System.Net.Security.SslStream.<FillBufferAsync>g__InternalFillBufferAsync|215_0[TReadAdapter](TReadAdapter adap, ValueTask`1 task, Int32 min, Int32 initial)
   at System.Net.Security.SslStream.ReadAsyncInternal[TReadAdapter](TReadAdapter adapter, Memory`1 buffer)
   at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

cc @dotnet/dnceng

Unrelated test failure on netcoreapp-Linux-Debug-x64-RedHat.7.Amd64.Open: System.Text.RegularExpressions.Tests.RegexCacheTests.Ctor_Cache_Uses_dictionary_linked_list_switch_does_not_throw

https://github.com/dotnet/corefx/issues/41981

https://helix.dot.net/api/2019-06-17/jobs/15bae51c-6a2d-4d34-9e22-f01a622747d8/workitems/System.Text.RegularExpressions.Tests/console

   System.Text.RegularExpressions.Tests: [Long Running Test] 'System.Text.RegularExpressions.Tests.RegexCacheTests.Ctor_Cache_Uses_dictionary_linked_list_switch_does_not_throw', Elapsed: 00:02:46
[Long Running Test] 'System.Text.RegularExpressions.Tests.RegexMatchTests.Match_Timeout_Loop_Throws', Elapsed: 00:02:46
    System.Text.RegularExpressions.Tests.RegexCacheTests.Ctor_Cache_Uses_dictionary_linked_list_switch_does_not_throw [FAIL]
      Timed out at 10/22/2019 5:54:39 AM after 60000ms waiting for remote process.
      	Process ID: 2117
      	Handle: 892
      
      Stack Trace:
        /_/src/Microsoft.DotNet.RemoteExecutor/src/RemoteInvokeHandle.cs(152,0): at Microsoft.DotNet.RemoteExecutor.RemoteInvokeHandle.Dispose(Boolean disposing)
        /_/src/Microsoft.DotNet.RemoteExecutor/src/RemoteInvokeHandle.cs(55,0): at Microsoft.DotNet.RemoteExecutor.RemoteInvokeHandle.Dispose()
        /_/src/System.Text.RegularExpressions/tests/Regex.Cache.Tests.cs(106,0): at System.Text.RegularExpressions.Tests.RegexCacheTests.Ctor_Cache_Uses_dictionary_linked_list_switch_does_not_throw()
  Finished:    System.Text.RegularExpressions.Tests
=== TEST EXECUTION SUMMARY ===
   System.Text.RegularExpressions.Tests  Total: 1454, Errors: 0, Failed: 1, Skipped: 0, Time: 186.432s
~/work/15bae51c-6a2d-4d34-9e22-f01a622747d8/Work/a46ce5a8-72f4-429c-8764-4786c3af5804/Exec
----- end Tue Oct 22 05:56:13 UTC 2019 ----- exit code 1 ----------------------------------------------------------

@ahsonkhan
Copy link
Author

Any other feedback?

{
Debug.Assert(Sse2.IsSupported);

Vector128<short> mask = Sse2.CompareLessThan(sourceValue, s_mask_UInt16_0x20); // Space ' ', anything in the control characters range
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment could add "anything above short.MaxValue but less than or equal char.MaxValue" since a signed short is used.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mentioned that below (in CreateAsciiMask), but sure, can add the comment here too.

Vector128<sbyte> sourceValue = Sse2.LoadVector128(startingAddress);

Vector128<sbyte> mask = Sse2Helper.CreateAsciiMask(sourceValue);
int index = Sse2.MoveMask(mask.AsByte());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is AsByte called (since we just initialized for sbyte)?

Copy link
Author

@ahsonkhan ahsonkhan Oct 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This we can remove, since we can use the sbyte overload. Good catch :)

However, it doesn't have any impact on the disassembly (so there is literally zero perf impact of doing either).

@ahsonkhan ahsonkhan merged commit c4b93b6 into dotnet:master Oct 23, 2019
@ahsonkhan ahsonkhan deleted the OptimizeEscapingSTEW branch October 23, 2019 00:34
ahsonkhan added a commit to ahsonkhan/corefx that referenced this pull request Oct 23, 2019
…xed using Sse2 intrinsics. (dotnet#41933)

* Optimize FindFirstCharToEncode for JavaScriptEncoder.Default and Relaxed
using Sse2 intrinsics.

* Create an Sse2Helper and improve perf of TextEncoder and
AllowedCharactersBitmap

* Loop unroll FindFirstCharacterToEncode

* Improve code coverage.

* Add more tests for surrogate pairs and fix call to WillEncode.

* Address PR feedback - remove some code duplication.

* Move DefaultJavaScriptEncoder to separate file and override EncodeUtf8
with better caching.

* Add default replacement character as a test.

* Address nits.
@danmoseley
Copy link
Member

Did someone explicitly signoff before merging?

@steveharter
Copy link
Contributor

FWIW I was just going to approve it before it was merged.

picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…xed using Sse2 intrinsics. (dotnet/corefx#41933)

* Optimize FindFirstCharToEncode for JavaScriptEncoder.Default and Relaxed
using Sse2 intrinsics.

* Create an Sse2Helper and improve perf of TextEncoder and
AllowedCharactersBitmap

* Loop unroll FindFirstCharacterToEncode

* Improve code coverage.

* Add more tests for surrogate pairs and fix call to WillEncode.

* Address PR feedback - remove some code duplication.

* Move DefaultJavaScriptEncoder to separate file and override EncodeUtf8
with better caching.

* Add default replacement character as a test.

* Address nits.


Commit migrated from dotnet/corefx@c4b93b6
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants