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

Avoid unnecessary byte[] allocations in HttpContent #2493

Merged
merged 1 commit into from Aug 25, 2015

Conversation

Projects
None yet
8 participants
@justinvp
Collaborator

justinvp commented Jul 24, 2015

HttpContent.ReadAsStringAsync() has a bunch of calls to Encoding.GetPreamble() as part of its encoding detection. GetPreamble() creates a new byte[] each time it is called. These byte[] allocations can be avoided.

Show outdated Hide outdated src/System.Net.Http/src/System/Net/Http/HttpContent.cs
public readonly Encoding Encoding;
public readonly byte[] Preamble;
public EncodingPreamblePair(Encoding encoding, byte[] preamble)

This comment has been minimized.

@stephentoub

stephentoub Jul 24, 2015

Member

The only place you're constructing these, you're passing in SomeEncoding and SomeEncoding.GetPreamble(). You could simplify those call sites by just making the constructor be:

public EncodingPreamblePair(Encoding encoding)
{
    Encoding = encoding;
    Preamble = encoding.GetPreamble();
}

or you could add a constructor that does that:

public EncodingPreamblePair(Encoding encoding) : this(encoding, encoding.GetPreamble())
{
}

public EncodingPreamblePair(Encoding encoding, byte[] preamble)
{
    Encoding = encoding;
    Preamble = preamble;
}
@stephentoub

stephentoub Jul 24, 2015

Member

The only place you're constructing these, you're passing in SomeEncoding and SomeEncoding.GetPreamble(). You could simplify those call sites by just making the constructor be:

public EncodingPreamblePair(Encoding encoding)
{
    Encoding = encoding;
    Preamble = encoding.GetPreamble();
}

or you could add a constructor that does that:

public EncodingPreamblePair(Encoding encoding) : this(encoding, encoding.GetPreamble())
{
}

public EncodingPreamblePair(Encoding encoding, byte[] preamble)
{
    Encoding = encoding;
    Preamble = preamble;
}

This comment has been minimized.

@justinvp

justinvp Jul 24, 2015

Collaborator

Ah, yes, thanks! (I didn't notice the possible simplification because I was originally using KeyValuePair<K,V>). Fixed.

@justinvp

justinvp Jul 24, 2015

Collaborator

Ah, yes, thanks! (I didn't notice the possible simplification because I was originally using KeyValuePair<K,V>). Fixed.

Show outdated Hide outdated src/System.Net.Http/src/System/Net/Http/HttpContent.cs
@@ -22,17 +22,17 @@ public abstract class HttpContent : IDisposable
internal const long MaxBufferSize = Int32.MaxValue;
internal static readonly Encoding DefaultStringEncoding = Encoding.UTF8;
// These encodings have Byte-Order-Markers that we will use to detect the encoding.
private static Encoding[] s_encodingsWithBom =
private static EncodingPreamblePair[] s_encodingPreamblePairs =

This comment has been minimized.

@Maxwe11

Maxwe11 Jul 24, 2015

Contributor

readonly

@Maxwe11

Maxwe11 Jul 24, 2015

Contributor

readonly

This comment has been minimized.

@justinvp

justinvp Jul 24, 2015

Collaborator

Fixed!

@justinvp

justinvp Jul 24, 2015

Collaborator

Fixed!

@davidsh

This comment has been minimized.

Show comment
Hide comment
Show outdated Hide outdated src/System.Net.Http/src/System/Net/Http/HttpContent.cs
@@ -568,5 +580,17 @@ private void CheckSize(int countToAdd)
}
}
}
private struct EncodingPreamblePair

This comment has been minimized.

@stephentoub

stephentoub Jul 24, 2015

Member

I'm not convinced this should be a struct. Since we're only ever constructing a handful of these things, but we're iterating through the array that stores them potentially a lot, seems like it could be better to keep the size of the data being enumerated at one instead of two references.

@stephentoub

stephentoub Jul 24, 2015

Member

I'm not convinced this should be a struct. Since we're only ever constructing a handful of these things, but we're iterating through the array that stores them potentially a lot, seems like it could be better to keep the size of the data being enumerated at one instead of two references.

This comment has been minimized.

@justinvp

justinvp Jul 24, 2015

Collaborator

What'd you have in mind? Two arrays (one for the encodings and one for the byte[] preambles, that share the same indexes)? Or just one array of byte[] preambles with known indexes to get the associated encoding?

@justinvp

justinvp Jul 24, 2015

Collaborator

What'd you have in mind? Two arrays (one for the encodings and one for the byte[] preambles, that share the same indexes)? Or just one array of byte[] preambles with known indexes to get the associated encoding?

This comment has been minimized.

@mikedn

mikedn Jul 24, 2015

Collaborator

struct is fine in this case, it's actually better than class as it avoids an additional indirection. struct would have been an issue if it was large and something like List<> would have been used instead of an array.

@mikedn

mikedn Jul 24, 2015

Collaborator

struct is fine in this case, it's actually better than class as it avoids an additional indirection. struct would have been an issue if it was large and something like List<> would have been used instead of an array.

This comment has been minimized.

@justinvp

justinvp Jul 24, 2015

Collaborator

I'll play around with a couple alternative approaches and measure.

@justinvp

justinvp Jul 24, 2015

Collaborator

I'll play around with a couple alternative approaches and measure.

This comment has been minimized.

@stephentoub

stephentoub Jul 24, 2015

Member

@mikedn, you're probably right, but I'm not sure what's wrong with my microbenchmarking then. Here's my example:

using System;
using System.Diagnostics;
using System.Linq;

static class Test
{
    sealed class WrapperClass
    {
        public object Object1;
        public object Object2;
    }

    struct WrapperStruct
    {
        public object Object1;
        public object Object2;
    }

    static void Main()
    {
        var classArr = Enumerable.Range(0, 10).Select(_ => new WrapperClass { Object1 = new object(), Object2 = new object() }).ToArray();
        var structArr = Enumerable.Range(0, 10).Select(_ => new WrapperStruct { Object1 = new object(), Object2 = new object() }).ToArray();
        var sw = new Stopwatch();
        const int Iters = 100000000;

        while (true)
        {
            sw.Restart();
            for (int i = 0; i < Iters; i++)
            {
                foreach (var item in structArr)
                {
                    object obj1 = item.Object1;
                    object obj2 = item.Object2;
                }
            }
            Console.WriteLine("Struct : " + sw.ElapsedMilliseconds);

            sw.Restart();
            for (int i = 0; i < Iters; i++)
            {
                foreach (var item in classArr)
                {
                    object obj1 = item.Object1;
                    object obj2 = item.Object2;
                }
            }
            Console.WriteLine("Class  : " + sw.ElapsedMilliseconds);

            Console.WriteLine();
        }
    }
}

With VS2015 RTM, on my machine the 32-bit JIT results in the class being 3x the speed of the struct version, and the 64-bit JIT results in it being 1.25x faster. Do you see different results?

@stephentoub

stephentoub Jul 24, 2015

Member

@mikedn, you're probably right, but I'm not sure what's wrong with my microbenchmarking then. Here's my example:

using System;
using System.Diagnostics;
using System.Linq;

static class Test
{
    sealed class WrapperClass
    {
        public object Object1;
        public object Object2;
    }

    struct WrapperStruct
    {
        public object Object1;
        public object Object2;
    }

    static void Main()
    {
        var classArr = Enumerable.Range(0, 10).Select(_ => new WrapperClass { Object1 = new object(), Object2 = new object() }).ToArray();
        var structArr = Enumerable.Range(0, 10).Select(_ => new WrapperStruct { Object1 = new object(), Object2 = new object() }).ToArray();
        var sw = new Stopwatch();
        const int Iters = 100000000;

        while (true)
        {
            sw.Restart();
            for (int i = 0; i < Iters; i++)
            {
                foreach (var item in structArr)
                {
                    object obj1 = item.Object1;
                    object obj2 = item.Object2;
                }
            }
            Console.WriteLine("Struct : " + sw.ElapsedMilliseconds);

            sw.Restart();
            for (int i = 0; i < Iters; i++)
            {
                foreach (var item in classArr)
                {
                    object obj1 = item.Object1;
                    object obj2 = item.Object2;
                }
            }
            Console.WriteLine("Class  : " + sw.ElapsedMilliseconds);

            Console.WriteLine();
        }
    }
}

With VS2015 RTM, on my machine the 32-bit JIT results in the class being 3x the speed of the struct version, and the 64-bit JIT results in it being 1.25x faster. Do you see different results?

This comment has been minimized.

@mikedn

mikedn Jul 24, 2015

Collaborator

JIT fail. In the class case it partially eliminates some the code inside the loop, in the struct case it insists on copying the struct fields to obj1 and obj2. If you change both loop bodies to

if (item.Object1 == item.Object2)
    Console.WriteLine("eq");

you'll get similar results for both struct and class. The class case is still a tiny bit faster due to other JIT issues but the difference is small enough that avoiding some heap allocations seems preferable.

That's with RyuJIT, I haven't tested with the x86 JIT. Is this code ever supposed to run on x86? As is now CoreCLR doesn't quite support x86.

@mikedn

mikedn Jul 24, 2015

Collaborator

JIT fail. In the class case it partially eliminates some the code inside the loop, in the struct case it insists on copying the struct fields to obj1 and obj2. If you change both loop bodies to

if (item.Object1 == item.Object2)
    Console.WriteLine("eq");

you'll get similar results for both struct and class. The class case is still a tiny bit faster due to other JIT issues but the difference is small enough that avoiding some heap allocations seems preferable.

That's with RyuJIT, I haven't tested with the x86 JIT. Is this code ever supposed to run on x86? As is now CoreCLR doesn't quite support x86.

This comment has been minimized.

@stephentoub

stephentoub Jul 24, 2015

Member

Is this code ever supposed to run on x86? As is now CoreCLR doesn't quite support x86.

CoreCLR is meant to support 32-bit, at least on Windows, and in fact we currently run most of our tests on Windows on 32-bit (though hopefully they'll soon run 64-bit by default). Plus many of these libraries are shared with .NET Native.

@stephentoub

stephentoub Jul 24, 2015

Member

Is this code ever supposed to run on x86? As is now CoreCLR doesn't quite support x86.

CoreCLR is meant to support 32-bit, at least on Windows, and in fact we currently run most of our tests on Windows on 32-bit (though hopefully they'll soon run 64-bit by default). Plus many of these libraries are shared with .NET Native.

This comment has been minimized.

@mikedn

mikedn Jul 24, 2015

Collaborator

Well, I checked the x86 results and it's funny, both struct and class generate good code yet the struct case is slower (1000ms vs 800ms). That's until you invert the order of the tests and then the struct case is slightly faster (730ms vs 770ms). Oh well, I guess we'll never get a realistic result by using such benchmarks. Anyone offers to flip a coin? 😄

@mikedn

mikedn Jul 24, 2015

Collaborator

Well, I checked the x86 results and it's funny, both struct and class generate good code yet the struct case is slower (1000ms vs 800ms). That's until you invert the order of the tests and then the struct case is slightly faster (730ms vs 770ms). Oh well, I guess we'll never get a realistic result by using such benchmarks. Anyone offers to flip a coin? 😄

This comment has been minimized.

@stephentoub

stephentoub Jul 24, 2015

Member

I flipped one 😄 The coin suggested sticking with struct.

@stephentoub

stephentoub Jul 24, 2015

Member

I flipped one 😄 The coin suggested sticking with struct.

Show outdated Hide outdated src/System.Net.Http/src/System/Net/Http/HttpContent.cs
{
foreach (EncodingPreamblePair pair in s_encodingPreamblePairs)
{
if (pair.Encoding.Equals(encoding))
Show outdated Hide outdated src/System.Net.Http/src/System/Net/Http/HttpContent.cs
@@ -134,7 +134,7 @@ public Task<string> ReadAsStringAsync()
// BOM characters may be present even if a charset was specified.
if (bomLength == -1)
{
byte[] preamble = encoding.GetPreamble();
byte[] preamble = GetPreamble(encoding);

This comment has been minimized.

@stephentoub

stephentoub Jul 24, 2015

Member

I'm not understanding something about the existing code. On the line:

encoding = encoding ?? DefaultStringEncoding;

we're checking if encoding is null, and if it is, we're using DefaultStringEncoding, which is UTF8. But UTF8 is in the encoding table, so in that fallback case, we would have already checked for UTF8 and found that the UTF8 BOM didn't match (if it did match, encoding wouldn't be null and bomLength would be > 0). Yet here we're falling back to DefaultStringEncoding and then going through the checks all over again. What am I missing? Shouldn't the logic instead be:

if (encoding == null)
{
    encoding = DefaultStringEncoding;
    bomLength = 0;
}
else if (bomLength == -1)
{
    ...
}

?

@stephentoub

stephentoub Jul 24, 2015

Member

I'm not understanding something about the existing code. On the line:

encoding = encoding ?? DefaultStringEncoding;

we're checking if encoding is null, and if it is, we're using DefaultStringEncoding, which is UTF8. But UTF8 is in the encoding table, so in that fallback case, we would have already checked for UTF8 and found that the UTF8 BOM didn't match (if it did match, encoding wouldn't be null and bomLength would be > 0). Yet here we're falling back to DefaultStringEncoding and then going through the checks all over again. What am I missing? Shouldn't the logic instead be:

if (encoding == null)
{
    encoding = DefaultStringEncoding;
    bomLength = 0;
}
else if (bomLength == -1)
{
    ...
}

?

@justinvp

This comment has been minimized.

Show comment
Hide comment
@justinvp

justinvp Jul 25, 2015

Collaborator

Updated with an alternative approach that I landed on after trying a few alternatives. It's a little more code, but minimal allocations, and a lot faster than my initial commit:

  • TryDetectEncoding is ~1.3-1.4x faster than looping through pairs.
  • GetPreambleLength is ~5-5.8x faster than looping through pairs and checking pair.Encoding.Equals(encoding) and ~3.5-3.6x faster than checking pair.Encoding.CodePage == encoding.CodePage.

PTAL

(Note: I intend to squash all commits before merging).

Collaborator

justinvp commented Jul 25, 2015

Updated with an alternative approach that I landed on after trying a few alternatives. It's a little more code, but minimal allocations, and a lot faster than my initial commit:

  • TryDetectEncoding is ~1.3-1.4x faster than looping through pairs.
  • GetPreambleLength is ~5-5.8x faster than looping through pairs and checking pair.Encoding.Equals(encoding) and ~3.5-3.6x faster than checking pair.Encoding.CodePage == encoding.CodePage.

PTAL

(Note: I intend to squash all commits before merging).

Show outdated Hide outdated src/System.Net.Http/src/System/Net/Http/HttpContent.cs
encoding = DefaultStringEncoding;
bomLength = 0;
}
else if (bomLength == -1)

This comment has been minimized.

@stephentoub

stephentoub Jul 25, 2015

Member

With the way this has been restructured, is this else if necessary? I'm wondering if you can just move the body that sets bomLength up into the try/catch earlier, e.g.

try
{
    encoding = Encoding.GetEncoding(innerThis.Headers.ContentType.CharSet);
    bomLength = GetPreambleLength(data, dataLength, encoding);
}

To me that makes all of this logic more clear:

  • If there was a header for encoding, we try to get the encoding and its associated preamble length.
  • If that wasn't successful, either because there wasn't a header or because we didn't recognize the encoding, we try to detect it from the data and get its associated BOM length
  • Finally, if that wasn't successful, we use a default encoding and BOM length.
@stephentoub

stephentoub Jul 25, 2015

Member

With the way this has been restructured, is this else if necessary? I'm wondering if you can just move the body that sets bomLength up into the try/catch earlier, e.g.

try
{
    encoding = Encoding.GetEncoding(innerThis.Headers.ContentType.CharSet);
    bomLength = GetPreambleLength(data, dataLength, encoding);
}

To me that makes all of this logic more clear:

  • If there was a header for encoding, we try to get the encoding and its associated preamble length.
  • If that wasn't successful, either because there wasn't a header or because we didn't recognize the encoding, we try to detect it from the data and get its associated BOM length
  • Finally, if that wasn't successful, we use a default encoding and BOM length.

This comment has been minimized.

@stephentoub

stephentoub Jul 25, 2015

Member

(I'd probably also add a comment where bomLength is set to 0, highlighting that DefaultStringEncoding is UTF8, but we already checked to see if it had a UTF8 BOM, so the bomLength is 0.)

@stephentoub

stephentoub Jul 25, 2015

Member

(I'd probably also add a comment where bomLength is set to 0, highlighting that DefaultStringEncoding is UTF8, but we already checked to see if it had a UTF8 BOM, so the bomLength is 0.)

This comment has been minimized.

@justinvp

justinvp Jul 25, 2015

Collaborator

Fixed. The logic is much clearer now, thanks for the feedback.

With the latest changes, the detection/fallback part looks like the following:

// If no content encoding is listed in the ContentType HTTP header, or no Content-Type header present,
// then check for a BOM in the data to figure out the encoding.
if (encoding == null)
{
    TryDetectEncoding(data, dataLength, ref encoding, ref bomLength);
}

// Use the default encoding if we couldn't detect one.
if (encoding == null)
{
    encoding = DefaultStringEncoding;

    // DefaultStringEncoding is UTF8, but we already checked to see if it had a UTF8 BOM,
    // so the bomLength is 0.
    bomLength = 0;
}

I'm mulling over changing TryDetectEncoding to actually return true/false, in which case the above could be:

// If no content encoding is listed in the ContentType HTTP header, or no Content-Type header present,
// then check for a BOM in the data to figure out the encoding.
if (encoding == null)
{
    if (!TryDetectEncoding(data, dataLength, ref encoding, ref bomLength))
    {
        // Use the default encoding if we couldn't detect one.
        encoding = DefaultStringEncoding;

        // DefaultStringEncoding is UTF8, but we already checked to see if it had a UTF8 BOM,
        // so the bomLength is 0.
        bomLength = 0;
    }
}

Do you have a preference?

@justinvp

justinvp Jul 25, 2015

Collaborator

Fixed. The logic is much clearer now, thanks for the feedback.

With the latest changes, the detection/fallback part looks like the following:

// If no content encoding is listed in the ContentType HTTP header, or no Content-Type header present,
// then check for a BOM in the data to figure out the encoding.
if (encoding == null)
{
    TryDetectEncoding(data, dataLength, ref encoding, ref bomLength);
}

// Use the default encoding if we couldn't detect one.
if (encoding == null)
{
    encoding = DefaultStringEncoding;

    // DefaultStringEncoding is UTF8, but we already checked to see if it had a UTF8 BOM,
    // so the bomLength is 0.
    bomLength = 0;
}

I'm mulling over changing TryDetectEncoding to actually return true/false, in which case the above could be:

// If no content encoding is listed in the ContentType HTTP header, or no Content-Type header present,
// then check for a BOM in the data to figure out the encoding.
if (encoding == null)
{
    if (!TryDetectEncoding(data, dataLength, ref encoding, ref bomLength))
    {
        // Use the default encoding if we couldn't detect one.
        encoding = DefaultStringEncoding;

        // DefaultStringEncoding is UTF8, but we already checked to see if it had a UTF8 BOM,
        // so the bomLength is 0.
        bomLength = 0;
    }
}

Do you have a preference?

This comment has been minimized.

@justinvp

justinvp Jul 25, 2015

Collaborator

Or, move the DefaultStringEncoding fallback directly inside TryDetectEncoding and keep it void (in which case I'd probably rename it just DetectEncoding).

// If no content encoding is listed in the ContentType HTTP header, or no Content-Type header present,
// then check for a BOM in the data to figure out the encoding with fallback to DefaultStringEncoding.
if (encoding == null)
{
    DetectEncoding(data, dataLength, out encoding, out bomLength);
}
@justinvp

justinvp Jul 25, 2015

Collaborator

Or, move the DefaultStringEncoding fallback directly inside TryDetectEncoding and keep it void (in which case I'd probably rename it just DetectEncoding).

// If no content encoding is listed in the ContentType HTTP header, or no Content-Type header present,
// then check for a BOM in the data to figure out the encoding with fallback to DefaultStringEncoding.
if (encoding == null)
{
    DetectEncoding(data, dataLength, out encoding, out bomLength);
}

This comment has been minimized.

@stephentoub

stephentoub Jul 26, 2015

Member

Do you have a preference?

My preference would be the Try version, as it makes it more clear to me at least what policy is being applied. If you did switch to the DetectEncoding version, my preference would be for it to return the encoding rather than using a ref, and to use out rather ref for the bomLength. Just my personal preference.

@stephentoub

stephentoub Jul 26, 2015

Member

Do you have a preference?

My preference would be the Try version, as it makes it more clear to me at least what policy is being applied. If you did switch to the DetectEncoding version, my preference would be for it to return the encoding rather than using a ref, and to use out rather ref for the bomLength. Just my personal preference.

@stephentoub

This comment has been minimized.

Show comment
Hide comment
Member

stephentoub commented Jul 25, 2015

LGTM

@justinvp

This comment has been minimized.

Show comment
Hide comment
@justinvp

justinvp Jul 28, 2015

Collaborator

I have an even faster version of the encoding detection (~2-2.6x faster). I'll update the PR tomorrow with the improvements (sorry for the additional churn).

Collaborator

justinvp commented Jul 28, 2015

I have an even faster version of the encoding detection (~2-2.6x faster). I'll update the PR tomorrow with the improvements (sorry for the additional churn).

@justinvp

This comment has been minimized.

Show comment
Hide comment
@justinvp

justinvp Jul 28, 2015

Collaborator

I added a new commit with the improvements. GetPreambleLength is ~1.2-1.5x faster than the previous commit and TryDetectEncoding is ~1.2-5.4x faster (x64 RyuJIT), depending on the data and encoding.

Collaborator

justinvp commented Jul 28, 2015

I added a new commit with the improvements. GetPreambleLength is ~1.2-1.5x faster than the previous commit and TryDetectEncoding is ~1.2-5.4x faster (x64 RyuJIT), depending on the data and encoding.

@davidsh davidsh added the 0 - Backlog label Aug 3, 2015

@davidsh

This comment has been minimized.

Show comment
Hide comment
@davidsh

davidsh Aug 24, 2015

Member

@stephentoub Can you please review the latest commits to this? It changed since your previous LGTM. I'm preparing final testing of this. Thx.

Member

davidsh commented Aug 24, 2015

@stephentoub Can you please review the latest commits to this? It changed since your previous LGTM. I'm preparing final testing of this. Thx.

@davidsh davidsh added 2 - In Progress and removed 0 - Backlog labels Aug 24, 2015

@stephentoub

This comment has been minimized.

Show comment
Hide comment
@stephentoub

stephentoub Aug 24, 2015

Member

LGTM. My only question is, due to the hardcoding of various constants here, should we add some more Debug.Asserts, potentially in a cctor that's only under an #if DEBUG, that validate that the constants we're using continue to match the data in the Encoding objects they represent? It's extremely unlikely / impossible I guess that these would change, so it's not required, just a thought.

Member

stephentoub commented Aug 24, 2015

LGTM. My only question is, due to the hardcoding of various constants here, should we add some more Debug.Asserts, potentially in a cctor that's only under an #if DEBUG, that validate that the constants we're using continue to match the data in the Encoding objects they represent? It's extremely unlikely / impossible I guess that these would change, so it's not required, just a thought.

@davidsh

This comment has been minimized.

Show comment
Hide comment
@davidsh

davidsh Aug 24, 2015

Member

@justinvp Can you comment on this? Thx.

Member

davidsh commented Aug 24, 2015

@justinvp Can you comment on this? Thx.

@justinvp

This comment has been minimized.

Show comment
Hide comment
@justinvp

justinvp Aug 25, 2015

Collaborator

Alright, I added asserts for the encoding constants. PR updated and squashed into a single commit.

Collaborator

justinvp commented Aug 25, 2015

Alright, I added asserts for the encoding constants. PR updated and squashed into a single commit.

@justinvp

This comment has been minimized.

Show comment
Hide comment
@justinvp

justinvp Aug 25, 2015

Collaborator

Looks like the CI build is failing for unrelated reasons:

ERROR: Failed to run DSL Script
java.util.concurrent.ExecutionException: org.codehaus.groovy.runtime.InvokerInvocationException: com.cloudbees.plugins.flow.JobExecutionFailureException
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:188)
    at java_util_concurrent_Future$get.call(Unknown Source)
    at com.cloudbees.plugins.flow.FlowDelegate$_parallel_closure6.doCall(FlowDSL.groovy:440)
    at sun.reflect.GeneratedMethodAccessor578.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at groovy.lang.Closure.call(Closure.java:415)
    at groovy.lang.Closure.call(Closure.java:428)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1379)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1351)
    at org.codehaus.groovy.runtime.dgm$170.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:271)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at com.cloudbees.plugins.flow.FlowDelegate.parallel(FlowDSL.groovy:438)
    at sun.reflect.GeneratedMethodAccessor666.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1079)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:723)
    at com.cloudbees.plugins.flow.FlowDelegate.invokeMethod(FlowDSL.groovy)
    at hudson.util.spring.ClosureScript.invokeMethod(ClosureScript.java:83)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:72)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:145)
    at Script1.run(Script1.groovy:1)
    at Script1$run.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at Script1$run.call(Unknown Source)
    at com.cloudbees.plugins.flow.FlowDSL.executeFlowScript(FlowDSL.groovy:84)
    at com.cloudbees.plugins.flow.FlowRun$BuildWithWorkspaceRunnerImpl.doRun(FlowRun.java:180)
    at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:537)
    at hudson.model.Run.execute(Run.java:1741)
    at hudson.model.Run.run(Run.java:1679)
    at com.cloudbees.plugins.flow.FlowRun.run(FlowRun.java:153)
    at hudson.model.ResourceController.execute(ResourceController.java:98)
    at hudson.model.Executor.run(Executor.java:381)
Caused by: org.codehaus.groovy.runtime.InvokerInvocationException: com.cloudbees.plugins.flow.JobExecutionFailureException
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:97)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at groovy.lang.Closure.call(Closure.java:415)
    at groovy.lang.Closure.call(Closure.java:409)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)
Caused by: com.cloudbees.plugins.flow.JobExecutionFailureException
    at sun.reflect.GeneratedConstructorAccessor394.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
    at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:186)
    at com.cloudbees.plugins.flow.FlowDelegate.fail(FlowDSL.groovy:171)
    at com.cloudbees.plugins.flow.FlowDelegate.statusCheck(FlowDSL.groovy:205)
    at com.cloudbees.plugins.flow.FlowDelegate.build(FlowDSL.groovy:210)
    at sun.reflect.GeneratedMethodAccessor584.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1079)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:723)
    at com.cloudbees.plugins.flow.FlowDelegate.invokeMethod(FlowDSL.groovy)
    at hudson.util.spring.ClosureScript.invokeMethod(ClosureScript.java:83)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeOnDelegationObjects(ClosureMetaClass.java:407)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:346)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:145)
    at Script1$_run_closure1.doCall(Script1.groovy:17)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
    at Script1$_run_closure1.doCall(Script1.groovy)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:39)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:54)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
    at com.cloudbees.plugins.flow.FlowDelegate$_parallel_closure5_closure7.doCall(FlowDSL.groovy:427)
    at sun.reflect.GeneratedMethodAccessor552.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
    at com.cloudbees.plugins.flow.FlowDelegate$_parallel_closure5_closure7.doCall(FlowDSL.groovy)
    at sun.reflect.GeneratedMethodAccessor549.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    ... 9 more
Collaborator

justinvp commented Aug 25, 2015

Looks like the CI build is failing for unrelated reasons:

ERROR: Failed to run DSL Script
java.util.concurrent.ExecutionException: org.codehaus.groovy.runtime.InvokerInvocationException: com.cloudbees.plugins.flow.JobExecutionFailureException
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:188)
    at java_util_concurrent_Future$get.call(Unknown Source)
    at com.cloudbees.plugins.flow.FlowDelegate$_parallel_closure6.doCall(FlowDSL.groovy:440)
    at sun.reflect.GeneratedMethodAccessor578.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at groovy.lang.Closure.call(Closure.java:415)
    at groovy.lang.Closure.call(Closure.java:428)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1379)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1351)
    at org.codehaus.groovy.runtime.dgm$170.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:271)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at com.cloudbees.plugins.flow.FlowDelegate.parallel(FlowDSL.groovy:438)
    at sun.reflect.GeneratedMethodAccessor666.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1079)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:723)
    at com.cloudbees.plugins.flow.FlowDelegate.invokeMethod(FlowDSL.groovy)
    at hudson.util.spring.ClosureScript.invokeMethod(ClosureScript.java:83)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:72)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:145)
    at Script1.run(Script1.groovy:1)
    at Script1$run.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at Script1$run.call(Unknown Source)
    at com.cloudbees.plugins.flow.FlowDSL.executeFlowScript(FlowDSL.groovy:84)
    at com.cloudbees.plugins.flow.FlowRun$BuildWithWorkspaceRunnerImpl.doRun(FlowRun.java:180)
    at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:537)
    at hudson.model.Run.execute(Run.java:1741)
    at hudson.model.Run.run(Run.java:1679)
    at com.cloudbees.plugins.flow.FlowRun.run(FlowRun.java:153)
    at hudson.model.ResourceController.execute(ResourceController.java:98)
    at hudson.model.Executor.run(Executor.java:381)
Caused by: org.codehaus.groovy.runtime.InvokerInvocationException: com.cloudbees.plugins.flow.JobExecutionFailureException
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:97)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at groovy.lang.Closure.call(Closure.java:415)
    at groovy.lang.Closure.call(Closure.java:409)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)
Caused by: com.cloudbees.plugins.flow.JobExecutionFailureException
    at sun.reflect.GeneratedConstructorAccessor394.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
    at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:186)
    at com.cloudbees.plugins.flow.FlowDelegate.fail(FlowDSL.groovy:171)
    at com.cloudbees.plugins.flow.FlowDelegate.statusCheck(FlowDSL.groovy:205)
    at com.cloudbees.plugins.flow.FlowDelegate.build(FlowDSL.groovy:210)
    at sun.reflect.GeneratedMethodAccessor584.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1079)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:723)
    at com.cloudbees.plugins.flow.FlowDelegate.invokeMethod(FlowDSL.groovy)
    at hudson.util.spring.ClosureScript.invokeMethod(ClosureScript.java:83)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeOnDelegationObjects(ClosureMetaClass.java:407)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:346)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:145)
    at Script1$_run_closure1.doCall(Script1.groovy:17)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
    at Script1$_run_closure1.doCall(Script1.groovy)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:39)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:54)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
    at com.cloudbees.plugins.flow.FlowDelegate$_parallel_closure5_closure7.doCall(FlowDSL.groovy:427)
    at sun.reflect.GeneratedMethodAccessor552.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:903)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
    at com.cloudbees.plugins.flow.FlowDelegate$_parallel_closure5_closure7.doCall(FlowDSL.groovy)
    at sun.reflect.GeneratedMethodAccessor549.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    ... 9 more
Avoid unnecessary byte[] allocations in HttpContent
HttpContent.ReadAsStringAsync() has a bunch of calls to
Encoding.GetPreamble() as part of its encoding detection.
GetPreamble() creates a new byte[] each time it is called.
These byte[] allocations can be avoided. Also, as part of this,
cleaned up and improved the performance of the encoding detection.
@stephentoub

This comment has been minimized.

Show comment
Hide comment
@stephentoub

stephentoub Aug 25, 2015

Member

@dotnet-bot test this please

Member

stephentoub commented Aug 25, 2015

@dotnet-bot test this please

@davidsh

This comment has been minimized.

Show comment
Hide comment
@davidsh

davidsh Aug 25, 2015

Member

LGTM

Member

davidsh commented Aug 25, 2015

LGTM

@stephentoub

This comment has been minimized.

Show comment
Hide comment
@stephentoub

stephentoub Aug 25, 2015

Member

LGTM2

Member

stephentoub commented Aug 25, 2015

LGTM2

davidsh added a commit that referenced this pull request Aug 25, 2015

Merge pull request #2493 from justinvp/http_preamble
Avoid unnecessary byte[] allocations in HttpContent

@davidsh davidsh merged commit 16abb16 into dotnet:master Aug 25, 2015

1 check passed

default Build finished. No test results found.
Details

@justinvp justinvp deleted the justinvp:http_preamble branch Oct 1, 2015

@karelz karelz modified the milestone: 1.0.0-rtm Dec 3, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment