Spock performance problems












3














I've some problems with performance with specifications implemented in Spock - I mean execution time in particular. After digging into the problem, I've noticed that it's somehow related with setting spec up - I don't mean setup() method in particular.



After this discovery, I added @Shared annotation to all the fields declared in the specification and it runs 2 times faster than before. Then, I thought, that performance problems may be related to ConcurrentHashMap or random* methods (from commons-lang3) but that wasn't the case.



In the end, in an act of desperation, I decorated all the fields in my specification in the following way:



class EntryFacadeSpec extends Specification {

static {
println(System.currentTimeMillis())
}
@Shared
def o = new Object()
static {
println(System.currentTimeMillis())
}
@Shared
private salesEntries = new InMemorySalesEntryRepository()
static {
println(System.currentTimeMillis())
}
@Shared
private purchaseEntries = new InMemoryPurchaseEntryRepository()
static {
println(System.currentTimeMillis())
}

...


What's interesting, no matter which field is declared as the first one it takes hundreds of milliseconds to initialize the field:



1542801494583
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495046
1542801495046
1542801495046
1542801495046
1542801495047
1542801495047


What's the problem? How to save this several hundred milliseconds?










share|improve this question
























  • Did you warmup the JVM before running this?
    – doelleri
    Nov 21 '18 at 14:32










  • @doelleri didn't do anything particular - can you tell how it can be done?
    – Opal
    Nov 21 '18 at 14:38






  • 1




    See stackoverflow.com/questions/504103/…
    – doelleri
    Nov 21 '18 at 14:40
















3














I've some problems with performance with specifications implemented in Spock - I mean execution time in particular. After digging into the problem, I've noticed that it's somehow related with setting spec up - I don't mean setup() method in particular.



After this discovery, I added @Shared annotation to all the fields declared in the specification and it runs 2 times faster than before. Then, I thought, that performance problems may be related to ConcurrentHashMap or random* methods (from commons-lang3) but that wasn't the case.



In the end, in an act of desperation, I decorated all the fields in my specification in the following way:



class EntryFacadeSpec extends Specification {

static {
println(System.currentTimeMillis())
}
@Shared
def o = new Object()
static {
println(System.currentTimeMillis())
}
@Shared
private salesEntries = new InMemorySalesEntryRepository()
static {
println(System.currentTimeMillis())
}
@Shared
private purchaseEntries = new InMemoryPurchaseEntryRepository()
static {
println(System.currentTimeMillis())
}

...


What's interesting, no matter which field is declared as the first one it takes hundreds of milliseconds to initialize the field:



1542801494583
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495046
1542801495046
1542801495046
1542801495046
1542801495047
1542801495047


What's the problem? How to save this several hundred milliseconds?










share|improve this question
























  • Did you warmup the JVM before running this?
    – doelleri
    Nov 21 '18 at 14:32










  • @doelleri didn't do anything particular - can you tell how it can be done?
    – Opal
    Nov 21 '18 at 14:38






  • 1




    See stackoverflow.com/questions/504103/…
    – doelleri
    Nov 21 '18 at 14:40














3












3








3


1





I've some problems with performance with specifications implemented in Spock - I mean execution time in particular. After digging into the problem, I've noticed that it's somehow related with setting spec up - I don't mean setup() method in particular.



After this discovery, I added @Shared annotation to all the fields declared in the specification and it runs 2 times faster than before. Then, I thought, that performance problems may be related to ConcurrentHashMap or random* methods (from commons-lang3) but that wasn't the case.



In the end, in an act of desperation, I decorated all the fields in my specification in the following way:



class EntryFacadeSpec extends Specification {

static {
println(System.currentTimeMillis())
}
@Shared
def o = new Object()
static {
println(System.currentTimeMillis())
}
@Shared
private salesEntries = new InMemorySalesEntryRepository()
static {
println(System.currentTimeMillis())
}
@Shared
private purchaseEntries = new InMemoryPurchaseEntryRepository()
static {
println(System.currentTimeMillis())
}

...


What's interesting, no matter which field is declared as the first one it takes hundreds of milliseconds to initialize the field:



1542801494583
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495046
1542801495046
1542801495046
1542801495046
1542801495047
1542801495047


What's the problem? How to save this several hundred milliseconds?










share|improve this question















I've some problems with performance with specifications implemented in Spock - I mean execution time in particular. After digging into the problem, I've noticed that it's somehow related with setting spec up - I don't mean setup() method in particular.



After this discovery, I added @Shared annotation to all the fields declared in the specification and it runs 2 times faster than before. Then, I thought, that performance problems may be related to ConcurrentHashMap or random* methods (from commons-lang3) but that wasn't the case.



In the end, in an act of desperation, I decorated all the fields in my specification in the following way:



class EntryFacadeSpec extends Specification {

static {
println(System.currentTimeMillis())
}
@Shared
def o = new Object()
static {
println(System.currentTimeMillis())
}
@Shared
private salesEntries = new InMemorySalesEntryRepository()
static {
println(System.currentTimeMillis())
}
@Shared
private purchaseEntries = new InMemoryPurchaseEntryRepository()
static {
println(System.currentTimeMillis())
}

...


What's interesting, no matter which field is declared as the first one it takes hundreds of milliseconds to initialize the field:



1542801494583
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495045
1542801495046
1542801495046
1542801495046
1542801495046
1542801495047
1542801495047


What's the problem? How to save this several hundred milliseconds?







performance testing groovy spock






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 21 '18 at 14:42









Michał Chmielarz

17316




17316










asked Nov 21 '18 at 12:47









Opal

52.3k20104117




52.3k20104117












  • Did you warmup the JVM before running this?
    – doelleri
    Nov 21 '18 at 14:32










  • @doelleri didn't do anything particular - can you tell how it can be done?
    – Opal
    Nov 21 '18 at 14:38






  • 1




    See stackoverflow.com/questions/504103/…
    – doelleri
    Nov 21 '18 at 14:40


















  • Did you warmup the JVM before running this?
    – doelleri
    Nov 21 '18 at 14:32










  • @doelleri didn't do anything particular - can you tell how it can be done?
    – Opal
    Nov 21 '18 at 14:38






  • 1




    See stackoverflow.com/questions/504103/…
    – doelleri
    Nov 21 '18 at 14:40
















Did you warmup the JVM before running this?
– doelleri
Nov 21 '18 at 14:32




Did you warmup the JVM before running this?
– doelleri
Nov 21 '18 at 14:32












@doelleri didn't do anything particular - can you tell how it can be done?
– Opal
Nov 21 '18 at 14:38




@doelleri didn't do anything particular - can you tell how it can be done?
– Opal
Nov 21 '18 at 14:38




1




1




See stackoverflow.com/questions/504103/…
– doelleri
Nov 21 '18 at 14:40




See stackoverflow.com/questions/504103/…
– doelleri
Nov 21 '18 at 14:40












1 Answer
1






active

oldest

votes


















4














TL;DR



Calling printlnin the first static block initializes about 30k+ objects related to Groovy Development Kit. It can take 50 ms at the minimum to finish, depending on horsepower of the laptop we run this test on.



The details



I couldn't reproduce the lag at the level of hundreds of milliseconds, but I was able to get a lag between 30 to 80 milliseconds. Let's start with the class I used in my local tests that reproduces your use case.



import spock.lang.Shared
import spock.lang.Specification

class EntryFacadeSpec extends Specification {

static {
println("${System.currentTimeMillis()} - start")
}

@Shared
def o = new Object()

static {
println("${System.currentTimeMillis()} - object")
}

@Shared
private salesEntries = new InMemorySalesEntryRepository()

static {
println("${System.currentTimeMillis()} - sales")
}

@Shared
private purchaseEntries = new InMemoryPurchaseEntryRepository()

static {
println("${System.currentTimeMillis()} - purchase")
}

def "test 1"() {
setup:
System.out.println(String.format('%d - test 1', System.currentTimeMillis()))

when:
def a = 1

then:
a == 1
}

def "test 2"() {
setup:
System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

when:
def a = 2

then:
a == 2
}

static class InMemorySalesEntryRepository {}

static class InMemoryPurchaseEntryRepository {}
}


Now, when I run it I see something like this in the console.



1542819186960 - start
1542819187019 - object
1542819187019 - sales
1542819187019 - purchase
1542819187035 - test 1
1542819187058 - test 2


We can see 59 milliseconds lag between the two first static blocks. It doesn't matter what is between these two blocks, because Groovy compiler merges all these 4 static blocks to a single static block that looks like this in plain Java:



static {
$getCallSiteArray()[0].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[1].call(System.class)}, new String{"", " - start"}));
$getCallSiteArray()[2].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[3].call(System.class)}, new String{"", " - object"}));
$getCallSiteArray()[4].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[5].call(System.class)}, new String{"", " - sales"}));
$getCallSiteArray()[6].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[7].call(System.class)}, new String{"", " - purchase"}));
}


So this 59 milliseconds lag happens between two first lines. Let's put a breakpoint in the first line and run a debugger.



enter image description here



Let's step over this line to the next line and let's see what happens:



enter image description here



We can see that invoking Groovy's println("${System.currentTimeMillis()} - start") caused creating more than 30k objects in the JVM. Now, let's step over the second line to the 3rd one to see what happens:



enter image description here



Only a few more objects got created.



This example shows that adding



static {
println(System.currentTimeMillis())
}


adds accidental complexity to the test setup and it does not show there is a lag between initialization of two class methods, but it creates this lag. However, the cost of initializing all Groovy related objects is something we can't completely avoid and it has to be paid somewhere. For instance, if we simplify the test to something like this:



import spock.lang.Specification

class EntryFacadeSpec extends Specification {

def "test 1"() {
setup:
println "asd ${System.currentTimeMillis()}"
println "asd ${System.currentTimeMillis()}"

when:
def a = 1

then:
a == 1
}

def "test 2"() {
setup:
System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

when:
def a = 2

then:
a == 2
}
}


and we put a breakpoint in the first println statement and step over to the next one, we will see something like this:



enter image description here



It still creates a few thousands of objects, but it is much less than in the first example because most of the objects we saw in the first example were already created before Spock executed the first method.



Overclocking Spock test performance



One of the first things we can do is to use static compilation. In case of my simple test it reduced execution time from 300 ms (non static compilation) to 227 ms approximately. Also the number of objects that have to be initialized is significantly reduced. If I run the same debugger scenario as the last one shown above with @CompileStatic added, I will get something like this:



enter image description here



It is still pretty significant, but we see that the number of objects initialized to invoke println method was dropped.



And the last thing worth mentioning. When we use static compilation and we want to avoid calling Groovy methods in the class static block to print some output we can use a combination of:



System.out.println(String.format("...", args))


because Groovy executes exactly this. On the other hand, following code in Groovy:



System.out.printf("...", args)


may look similar to the previous one, but it gets compiled to something like this (with static compilation enabled):



DefaultGroovyMethods.printf(System.out, "...", args)


The second case will be much slower when used in the class static block, because at this point Groovy jar is not yet loaded and the classloader has to resolve DefaultGroovyMethods class from the JAR file. When Spock executes test method it doesn't make much difference if you use System.out.println or DefaultGroovyMethods.printf, because Groovy classes are already loaded.



That is why if we rewrite your initial example to something like this:



import groovy.transform.CompileStatic
import spock.lang.Shared
import spock.lang.Specification

@CompileStatic
class EntryFacadeSpec extends Specification {

static {
System.out.println(String.format('%d - start', System.currentTimeMillis()))
}

@Shared
def o = new Object()

static {
System.out.println(String.format('%d - object', System.currentTimeMillis()))
}

@Shared
private salesEntries = new InMemorySalesEntryRepository()

static {
System.out.println(String.format('%d - sales', System.currentTimeMillis()))
}

@Shared
private purchaseEntries = new InMemoryPurchaseEntryRepository()

static {
System.out.println(String.format('%d - purchase', System.currentTimeMillis()))
}

def "test 1"() {
setup:
System.out.println(String.format('%d - test 1', System.currentTimeMillis()))

when:
def a = 1

then:
a == 1
}

def "test 2"() {
setup:
System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

when:
def a = 2

then:
a == 2
}

static class InMemorySalesEntryRepository {}

static class InMemoryPurchaseEntryRepository {}

}


we will get following console output:



1542821438552 - start
1542821438552 - object
1542821438552 - sales
1542821438552 - purchase
1542821438774 - test 1
1542821438786 - test 2


But what is more important, it doesn't log field initialization time, because Groovy compiles these 4 blocks to a single one like this:



static {
System.out.println(String.format("%d - start", System.currentTimeMillis()));
Object var10000 = null;
System.out.println(String.format("%d - object", System.currentTimeMillis()));
var10000 = null;
System.out.println(String.format("%d - sales", System.currentTimeMillis()));
var10000 = null;
System.out.println(String.format("%d - purchase", System.currentTimeMillis()));
var10000 = null;
}


There is no lag between the 1st and 2nd call, because there is no need to load Groovy classes at this point.






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53412363%2fspock-performance-problems%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    4














    TL;DR



    Calling printlnin the first static block initializes about 30k+ objects related to Groovy Development Kit. It can take 50 ms at the minimum to finish, depending on horsepower of the laptop we run this test on.



    The details



    I couldn't reproduce the lag at the level of hundreds of milliseconds, but I was able to get a lag between 30 to 80 milliseconds. Let's start with the class I used in my local tests that reproduces your use case.



    import spock.lang.Shared
    import spock.lang.Specification

    class EntryFacadeSpec extends Specification {

    static {
    println("${System.currentTimeMillis()} - start")
    }

    @Shared
    def o = new Object()

    static {
    println("${System.currentTimeMillis()} - object")
    }

    @Shared
    private salesEntries = new InMemorySalesEntryRepository()

    static {
    println("${System.currentTimeMillis()} - sales")
    }

    @Shared
    private purchaseEntries = new InMemoryPurchaseEntryRepository()

    static {
    println("${System.currentTimeMillis()} - purchase")
    }

    def "test 1"() {
    setup:
    System.out.println(String.format('%d - test 1', System.currentTimeMillis()))

    when:
    def a = 1

    then:
    a == 1
    }

    def "test 2"() {
    setup:
    System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

    when:
    def a = 2

    then:
    a == 2
    }

    static class InMemorySalesEntryRepository {}

    static class InMemoryPurchaseEntryRepository {}
    }


    Now, when I run it I see something like this in the console.



    1542819186960 - start
    1542819187019 - object
    1542819187019 - sales
    1542819187019 - purchase
    1542819187035 - test 1
    1542819187058 - test 2


    We can see 59 milliseconds lag between the two first static blocks. It doesn't matter what is between these two blocks, because Groovy compiler merges all these 4 static blocks to a single static block that looks like this in plain Java:



    static {
    $getCallSiteArray()[0].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[1].call(System.class)}, new String{"", " - start"}));
    $getCallSiteArray()[2].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[3].call(System.class)}, new String{"", " - object"}));
    $getCallSiteArray()[4].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[5].call(System.class)}, new String{"", " - sales"}));
    $getCallSiteArray()[6].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[7].call(System.class)}, new String{"", " - purchase"}));
    }


    So this 59 milliseconds lag happens between two first lines. Let's put a breakpoint in the first line and run a debugger.



    enter image description here



    Let's step over this line to the next line and let's see what happens:



    enter image description here



    We can see that invoking Groovy's println("${System.currentTimeMillis()} - start") caused creating more than 30k objects in the JVM. Now, let's step over the second line to the 3rd one to see what happens:



    enter image description here



    Only a few more objects got created.



    This example shows that adding



    static {
    println(System.currentTimeMillis())
    }


    adds accidental complexity to the test setup and it does not show there is a lag between initialization of two class methods, but it creates this lag. However, the cost of initializing all Groovy related objects is something we can't completely avoid and it has to be paid somewhere. For instance, if we simplify the test to something like this:



    import spock.lang.Specification

    class EntryFacadeSpec extends Specification {

    def "test 1"() {
    setup:
    println "asd ${System.currentTimeMillis()}"
    println "asd ${System.currentTimeMillis()}"

    when:
    def a = 1

    then:
    a == 1
    }

    def "test 2"() {
    setup:
    System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

    when:
    def a = 2

    then:
    a == 2
    }
    }


    and we put a breakpoint in the first println statement and step over to the next one, we will see something like this:



    enter image description here



    It still creates a few thousands of objects, but it is much less than in the first example because most of the objects we saw in the first example were already created before Spock executed the first method.



    Overclocking Spock test performance



    One of the first things we can do is to use static compilation. In case of my simple test it reduced execution time from 300 ms (non static compilation) to 227 ms approximately. Also the number of objects that have to be initialized is significantly reduced. If I run the same debugger scenario as the last one shown above with @CompileStatic added, I will get something like this:



    enter image description here



    It is still pretty significant, but we see that the number of objects initialized to invoke println method was dropped.



    And the last thing worth mentioning. When we use static compilation and we want to avoid calling Groovy methods in the class static block to print some output we can use a combination of:



    System.out.println(String.format("...", args))


    because Groovy executes exactly this. On the other hand, following code in Groovy:



    System.out.printf("...", args)


    may look similar to the previous one, but it gets compiled to something like this (with static compilation enabled):



    DefaultGroovyMethods.printf(System.out, "...", args)


    The second case will be much slower when used in the class static block, because at this point Groovy jar is not yet loaded and the classloader has to resolve DefaultGroovyMethods class from the JAR file. When Spock executes test method it doesn't make much difference if you use System.out.println or DefaultGroovyMethods.printf, because Groovy classes are already loaded.



    That is why if we rewrite your initial example to something like this:



    import groovy.transform.CompileStatic
    import spock.lang.Shared
    import spock.lang.Specification

    @CompileStatic
    class EntryFacadeSpec extends Specification {

    static {
    System.out.println(String.format('%d - start', System.currentTimeMillis()))
    }

    @Shared
    def o = new Object()

    static {
    System.out.println(String.format('%d - object', System.currentTimeMillis()))
    }

    @Shared
    private salesEntries = new InMemorySalesEntryRepository()

    static {
    System.out.println(String.format('%d - sales', System.currentTimeMillis()))
    }

    @Shared
    private purchaseEntries = new InMemoryPurchaseEntryRepository()

    static {
    System.out.println(String.format('%d - purchase', System.currentTimeMillis()))
    }

    def "test 1"() {
    setup:
    System.out.println(String.format('%d - test 1', System.currentTimeMillis()))

    when:
    def a = 1

    then:
    a == 1
    }

    def "test 2"() {
    setup:
    System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

    when:
    def a = 2

    then:
    a == 2
    }

    static class InMemorySalesEntryRepository {}

    static class InMemoryPurchaseEntryRepository {}

    }


    we will get following console output:



    1542821438552 - start
    1542821438552 - object
    1542821438552 - sales
    1542821438552 - purchase
    1542821438774 - test 1
    1542821438786 - test 2


    But what is more important, it doesn't log field initialization time, because Groovy compiles these 4 blocks to a single one like this:



    static {
    System.out.println(String.format("%d - start", System.currentTimeMillis()));
    Object var10000 = null;
    System.out.println(String.format("%d - object", System.currentTimeMillis()));
    var10000 = null;
    System.out.println(String.format("%d - sales", System.currentTimeMillis()));
    var10000 = null;
    System.out.println(String.format("%d - purchase", System.currentTimeMillis()));
    var10000 = null;
    }


    There is no lag between the 1st and 2nd call, because there is no need to load Groovy classes at this point.






    share|improve this answer




























      4














      TL;DR



      Calling printlnin the first static block initializes about 30k+ objects related to Groovy Development Kit. It can take 50 ms at the minimum to finish, depending on horsepower of the laptop we run this test on.



      The details



      I couldn't reproduce the lag at the level of hundreds of milliseconds, but I was able to get a lag between 30 to 80 milliseconds. Let's start with the class I used in my local tests that reproduces your use case.



      import spock.lang.Shared
      import spock.lang.Specification

      class EntryFacadeSpec extends Specification {

      static {
      println("${System.currentTimeMillis()} - start")
      }

      @Shared
      def o = new Object()

      static {
      println("${System.currentTimeMillis()} - object")
      }

      @Shared
      private salesEntries = new InMemorySalesEntryRepository()

      static {
      println("${System.currentTimeMillis()} - sales")
      }

      @Shared
      private purchaseEntries = new InMemoryPurchaseEntryRepository()

      static {
      println("${System.currentTimeMillis()} - purchase")
      }

      def "test 1"() {
      setup:
      System.out.println(String.format('%d - test 1', System.currentTimeMillis()))

      when:
      def a = 1

      then:
      a == 1
      }

      def "test 2"() {
      setup:
      System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

      when:
      def a = 2

      then:
      a == 2
      }

      static class InMemorySalesEntryRepository {}

      static class InMemoryPurchaseEntryRepository {}
      }


      Now, when I run it I see something like this in the console.



      1542819186960 - start
      1542819187019 - object
      1542819187019 - sales
      1542819187019 - purchase
      1542819187035 - test 1
      1542819187058 - test 2


      We can see 59 milliseconds lag between the two first static blocks. It doesn't matter what is between these two blocks, because Groovy compiler merges all these 4 static blocks to a single static block that looks like this in plain Java:



      static {
      $getCallSiteArray()[0].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[1].call(System.class)}, new String{"", " - start"}));
      $getCallSiteArray()[2].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[3].call(System.class)}, new String{"", " - object"}));
      $getCallSiteArray()[4].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[5].call(System.class)}, new String{"", " - sales"}));
      $getCallSiteArray()[6].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[7].call(System.class)}, new String{"", " - purchase"}));
      }


      So this 59 milliseconds lag happens between two first lines. Let's put a breakpoint in the first line and run a debugger.



      enter image description here



      Let's step over this line to the next line and let's see what happens:



      enter image description here



      We can see that invoking Groovy's println("${System.currentTimeMillis()} - start") caused creating more than 30k objects in the JVM. Now, let's step over the second line to the 3rd one to see what happens:



      enter image description here



      Only a few more objects got created.



      This example shows that adding



      static {
      println(System.currentTimeMillis())
      }


      adds accidental complexity to the test setup and it does not show there is a lag between initialization of two class methods, but it creates this lag. However, the cost of initializing all Groovy related objects is something we can't completely avoid and it has to be paid somewhere. For instance, if we simplify the test to something like this:



      import spock.lang.Specification

      class EntryFacadeSpec extends Specification {

      def "test 1"() {
      setup:
      println "asd ${System.currentTimeMillis()}"
      println "asd ${System.currentTimeMillis()}"

      when:
      def a = 1

      then:
      a == 1
      }

      def "test 2"() {
      setup:
      System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

      when:
      def a = 2

      then:
      a == 2
      }
      }


      and we put a breakpoint in the first println statement and step over to the next one, we will see something like this:



      enter image description here



      It still creates a few thousands of objects, but it is much less than in the first example because most of the objects we saw in the first example were already created before Spock executed the first method.



      Overclocking Spock test performance



      One of the first things we can do is to use static compilation. In case of my simple test it reduced execution time from 300 ms (non static compilation) to 227 ms approximately. Also the number of objects that have to be initialized is significantly reduced. If I run the same debugger scenario as the last one shown above with @CompileStatic added, I will get something like this:



      enter image description here



      It is still pretty significant, but we see that the number of objects initialized to invoke println method was dropped.



      And the last thing worth mentioning. When we use static compilation and we want to avoid calling Groovy methods in the class static block to print some output we can use a combination of:



      System.out.println(String.format("...", args))


      because Groovy executes exactly this. On the other hand, following code in Groovy:



      System.out.printf("...", args)


      may look similar to the previous one, but it gets compiled to something like this (with static compilation enabled):



      DefaultGroovyMethods.printf(System.out, "...", args)


      The second case will be much slower when used in the class static block, because at this point Groovy jar is not yet loaded and the classloader has to resolve DefaultGroovyMethods class from the JAR file. When Spock executes test method it doesn't make much difference if you use System.out.println or DefaultGroovyMethods.printf, because Groovy classes are already loaded.



      That is why if we rewrite your initial example to something like this:



      import groovy.transform.CompileStatic
      import spock.lang.Shared
      import spock.lang.Specification

      @CompileStatic
      class EntryFacadeSpec extends Specification {

      static {
      System.out.println(String.format('%d - start', System.currentTimeMillis()))
      }

      @Shared
      def o = new Object()

      static {
      System.out.println(String.format('%d - object', System.currentTimeMillis()))
      }

      @Shared
      private salesEntries = new InMemorySalesEntryRepository()

      static {
      System.out.println(String.format('%d - sales', System.currentTimeMillis()))
      }

      @Shared
      private purchaseEntries = new InMemoryPurchaseEntryRepository()

      static {
      System.out.println(String.format('%d - purchase', System.currentTimeMillis()))
      }

      def "test 1"() {
      setup:
      System.out.println(String.format('%d - test 1', System.currentTimeMillis()))

      when:
      def a = 1

      then:
      a == 1
      }

      def "test 2"() {
      setup:
      System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

      when:
      def a = 2

      then:
      a == 2
      }

      static class InMemorySalesEntryRepository {}

      static class InMemoryPurchaseEntryRepository {}

      }


      we will get following console output:



      1542821438552 - start
      1542821438552 - object
      1542821438552 - sales
      1542821438552 - purchase
      1542821438774 - test 1
      1542821438786 - test 2


      But what is more important, it doesn't log field initialization time, because Groovy compiles these 4 blocks to a single one like this:



      static {
      System.out.println(String.format("%d - start", System.currentTimeMillis()));
      Object var10000 = null;
      System.out.println(String.format("%d - object", System.currentTimeMillis()));
      var10000 = null;
      System.out.println(String.format("%d - sales", System.currentTimeMillis()));
      var10000 = null;
      System.out.println(String.format("%d - purchase", System.currentTimeMillis()));
      var10000 = null;
      }


      There is no lag between the 1st and 2nd call, because there is no need to load Groovy classes at this point.






      share|improve this answer


























        4












        4








        4






        TL;DR



        Calling printlnin the first static block initializes about 30k+ objects related to Groovy Development Kit. It can take 50 ms at the minimum to finish, depending on horsepower of the laptop we run this test on.



        The details



        I couldn't reproduce the lag at the level of hundreds of milliseconds, but I was able to get a lag between 30 to 80 milliseconds. Let's start with the class I used in my local tests that reproduces your use case.



        import spock.lang.Shared
        import spock.lang.Specification

        class EntryFacadeSpec extends Specification {

        static {
        println("${System.currentTimeMillis()} - start")
        }

        @Shared
        def o = new Object()

        static {
        println("${System.currentTimeMillis()} - object")
        }

        @Shared
        private salesEntries = new InMemorySalesEntryRepository()

        static {
        println("${System.currentTimeMillis()} - sales")
        }

        @Shared
        private purchaseEntries = new InMemoryPurchaseEntryRepository()

        static {
        println("${System.currentTimeMillis()} - purchase")
        }

        def "test 1"() {
        setup:
        System.out.println(String.format('%d - test 1', System.currentTimeMillis()))

        when:
        def a = 1

        then:
        a == 1
        }

        def "test 2"() {
        setup:
        System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

        when:
        def a = 2

        then:
        a == 2
        }

        static class InMemorySalesEntryRepository {}

        static class InMemoryPurchaseEntryRepository {}
        }


        Now, when I run it I see something like this in the console.



        1542819186960 - start
        1542819187019 - object
        1542819187019 - sales
        1542819187019 - purchase
        1542819187035 - test 1
        1542819187058 - test 2


        We can see 59 milliseconds lag between the two first static blocks. It doesn't matter what is between these two blocks, because Groovy compiler merges all these 4 static blocks to a single static block that looks like this in plain Java:



        static {
        $getCallSiteArray()[0].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[1].call(System.class)}, new String{"", " - start"}));
        $getCallSiteArray()[2].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[3].call(System.class)}, new String{"", " - object"}));
        $getCallSiteArray()[4].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[5].call(System.class)}, new String{"", " - sales"}));
        $getCallSiteArray()[6].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[7].call(System.class)}, new String{"", " - purchase"}));
        }


        So this 59 milliseconds lag happens between two first lines. Let's put a breakpoint in the first line and run a debugger.



        enter image description here



        Let's step over this line to the next line and let's see what happens:



        enter image description here



        We can see that invoking Groovy's println("${System.currentTimeMillis()} - start") caused creating more than 30k objects in the JVM. Now, let's step over the second line to the 3rd one to see what happens:



        enter image description here



        Only a few more objects got created.



        This example shows that adding



        static {
        println(System.currentTimeMillis())
        }


        adds accidental complexity to the test setup and it does not show there is a lag between initialization of two class methods, but it creates this lag. However, the cost of initializing all Groovy related objects is something we can't completely avoid and it has to be paid somewhere. For instance, if we simplify the test to something like this:



        import spock.lang.Specification

        class EntryFacadeSpec extends Specification {

        def "test 1"() {
        setup:
        println "asd ${System.currentTimeMillis()}"
        println "asd ${System.currentTimeMillis()}"

        when:
        def a = 1

        then:
        a == 1
        }

        def "test 2"() {
        setup:
        System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

        when:
        def a = 2

        then:
        a == 2
        }
        }


        and we put a breakpoint in the first println statement and step over to the next one, we will see something like this:



        enter image description here



        It still creates a few thousands of objects, but it is much less than in the first example because most of the objects we saw in the first example were already created before Spock executed the first method.



        Overclocking Spock test performance



        One of the first things we can do is to use static compilation. In case of my simple test it reduced execution time from 300 ms (non static compilation) to 227 ms approximately. Also the number of objects that have to be initialized is significantly reduced. If I run the same debugger scenario as the last one shown above with @CompileStatic added, I will get something like this:



        enter image description here



        It is still pretty significant, but we see that the number of objects initialized to invoke println method was dropped.



        And the last thing worth mentioning. When we use static compilation and we want to avoid calling Groovy methods in the class static block to print some output we can use a combination of:



        System.out.println(String.format("...", args))


        because Groovy executes exactly this. On the other hand, following code in Groovy:



        System.out.printf("...", args)


        may look similar to the previous one, but it gets compiled to something like this (with static compilation enabled):



        DefaultGroovyMethods.printf(System.out, "...", args)


        The second case will be much slower when used in the class static block, because at this point Groovy jar is not yet loaded and the classloader has to resolve DefaultGroovyMethods class from the JAR file. When Spock executes test method it doesn't make much difference if you use System.out.println or DefaultGroovyMethods.printf, because Groovy classes are already loaded.



        That is why if we rewrite your initial example to something like this:



        import groovy.transform.CompileStatic
        import spock.lang.Shared
        import spock.lang.Specification

        @CompileStatic
        class EntryFacadeSpec extends Specification {

        static {
        System.out.println(String.format('%d - start', System.currentTimeMillis()))
        }

        @Shared
        def o = new Object()

        static {
        System.out.println(String.format('%d - object', System.currentTimeMillis()))
        }

        @Shared
        private salesEntries = new InMemorySalesEntryRepository()

        static {
        System.out.println(String.format('%d - sales', System.currentTimeMillis()))
        }

        @Shared
        private purchaseEntries = new InMemoryPurchaseEntryRepository()

        static {
        System.out.println(String.format('%d - purchase', System.currentTimeMillis()))
        }

        def "test 1"() {
        setup:
        System.out.println(String.format('%d - test 1', System.currentTimeMillis()))

        when:
        def a = 1

        then:
        a == 1
        }

        def "test 2"() {
        setup:
        System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

        when:
        def a = 2

        then:
        a == 2
        }

        static class InMemorySalesEntryRepository {}

        static class InMemoryPurchaseEntryRepository {}

        }


        we will get following console output:



        1542821438552 - start
        1542821438552 - object
        1542821438552 - sales
        1542821438552 - purchase
        1542821438774 - test 1
        1542821438786 - test 2


        But what is more important, it doesn't log field initialization time, because Groovy compiles these 4 blocks to a single one like this:



        static {
        System.out.println(String.format("%d - start", System.currentTimeMillis()));
        Object var10000 = null;
        System.out.println(String.format("%d - object", System.currentTimeMillis()));
        var10000 = null;
        System.out.println(String.format("%d - sales", System.currentTimeMillis()));
        var10000 = null;
        System.out.println(String.format("%d - purchase", System.currentTimeMillis()));
        var10000 = null;
        }


        There is no lag between the 1st and 2nd call, because there is no need to load Groovy classes at this point.






        share|improve this answer














        TL;DR



        Calling printlnin the first static block initializes about 30k+ objects related to Groovy Development Kit. It can take 50 ms at the minimum to finish, depending on horsepower of the laptop we run this test on.



        The details



        I couldn't reproduce the lag at the level of hundreds of milliseconds, but I was able to get a lag between 30 to 80 milliseconds. Let's start with the class I used in my local tests that reproduces your use case.



        import spock.lang.Shared
        import spock.lang.Specification

        class EntryFacadeSpec extends Specification {

        static {
        println("${System.currentTimeMillis()} - start")
        }

        @Shared
        def o = new Object()

        static {
        println("${System.currentTimeMillis()} - object")
        }

        @Shared
        private salesEntries = new InMemorySalesEntryRepository()

        static {
        println("${System.currentTimeMillis()} - sales")
        }

        @Shared
        private purchaseEntries = new InMemoryPurchaseEntryRepository()

        static {
        println("${System.currentTimeMillis()} - purchase")
        }

        def "test 1"() {
        setup:
        System.out.println(String.format('%d - test 1', System.currentTimeMillis()))

        when:
        def a = 1

        then:
        a == 1
        }

        def "test 2"() {
        setup:
        System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

        when:
        def a = 2

        then:
        a == 2
        }

        static class InMemorySalesEntryRepository {}

        static class InMemoryPurchaseEntryRepository {}
        }


        Now, when I run it I see something like this in the console.



        1542819186960 - start
        1542819187019 - object
        1542819187019 - sales
        1542819187019 - purchase
        1542819187035 - test 1
        1542819187058 - test 2


        We can see 59 milliseconds lag between the two first static blocks. It doesn't matter what is between these two blocks, because Groovy compiler merges all these 4 static blocks to a single static block that looks like this in plain Java:



        static {
        $getCallSiteArray()[0].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[1].call(System.class)}, new String{"", " - start"}));
        $getCallSiteArray()[2].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[3].call(System.class)}, new String{"", " - object"}));
        $getCallSiteArray()[4].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[5].call(System.class)}, new String{"", " - sales"}));
        $getCallSiteArray()[6].callStatic(EntryFacadeSpec.class, new GStringImpl(new Object{$getCallSiteArray()[7].call(System.class)}, new String{"", " - purchase"}));
        }


        So this 59 milliseconds lag happens between two first lines. Let's put a breakpoint in the first line and run a debugger.



        enter image description here



        Let's step over this line to the next line and let's see what happens:



        enter image description here



        We can see that invoking Groovy's println("${System.currentTimeMillis()} - start") caused creating more than 30k objects in the JVM. Now, let's step over the second line to the 3rd one to see what happens:



        enter image description here



        Only a few more objects got created.



        This example shows that adding



        static {
        println(System.currentTimeMillis())
        }


        adds accidental complexity to the test setup and it does not show there is a lag between initialization of two class methods, but it creates this lag. However, the cost of initializing all Groovy related objects is something we can't completely avoid and it has to be paid somewhere. For instance, if we simplify the test to something like this:



        import spock.lang.Specification

        class EntryFacadeSpec extends Specification {

        def "test 1"() {
        setup:
        println "asd ${System.currentTimeMillis()}"
        println "asd ${System.currentTimeMillis()}"

        when:
        def a = 1

        then:
        a == 1
        }

        def "test 2"() {
        setup:
        System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

        when:
        def a = 2

        then:
        a == 2
        }
        }


        and we put a breakpoint in the first println statement and step over to the next one, we will see something like this:



        enter image description here



        It still creates a few thousands of objects, but it is much less than in the first example because most of the objects we saw in the first example were already created before Spock executed the first method.



        Overclocking Spock test performance



        One of the first things we can do is to use static compilation. In case of my simple test it reduced execution time from 300 ms (non static compilation) to 227 ms approximately. Also the number of objects that have to be initialized is significantly reduced. If I run the same debugger scenario as the last one shown above with @CompileStatic added, I will get something like this:



        enter image description here



        It is still pretty significant, but we see that the number of objects initialized to invoke println method was dropped.



        And the last thing worth mentioning. When we use static compilation and we want to avoid calling Groovy methods in the class static block to print some output we can use a combination of:



        System.out.println(String.format("...", args))


        because Groovy executes exactly this. On the other hand, following code in Groovy:



        System.out.printf("...", args)


        may look similar to the previous one, but it gets compiled to something like this (with static compilation enabled):



        DefaultGroovyMethods.printf(System.out, "...", args)


        The second case will be much slower when used in the class static block, because at this point Groovy jar is not yet loaded and the classloader has to resolve DefaultGroovyMethods class from the JAR file. When Spock executes test method it doesn't make much difference if you use System.out.println or DefaultGroovyMethods.printf, because Groovy classes are already loaded.



        That is why if we rewrite your initial example to something like this:



        import groovy.transform.CompileStatic
        import spock.lang.Shared
        import spock.lang.Specification

        @CompileStatic
        class EntryFacadeSpec extends Specification {

        static {
        System.out.println(String.format('%d - start', System.currentTimeMillis()))
        }

        @Shared
        def o = new Object()

        static {
        System.out.println(String.format('%d - object', System.currentTimeMillis()))
        }

        @Shared
        private salesEntries = new InMemorySalesEntryRepository()

        static {
        System.out.println(String.format('%d - sales', System.currentTimeMillis()))
        }

        @Shared
        private purchaseEntries = new InMemoryPurchaseEntryRepository()

        static {
        System.out.println(String.format('%d - purchase', System.currentTimeMillis()))
        }

        def "test 1"() {
        setup:
        System.out.println(String.format('%d - test 1', System.currentTimeMillis()))

        when:
        def a = 1

        then:
        a == 1
        }

        def "test 2"() {
        setup:
        System.out.println(String.format('%d - test 2', System.currentTimeMillis()))

        when:
        def a = 2

        then:
        a == 2
        }

        static class InMemorySalesEntryRepository {}

        static class InMemoryPurchaseEntryRepository {}

        }


        we will get following console output:



        1542821438552 - start
        1542821438552 - object
        1542821438552 - sales
        1542821438552 - purchase
        1542821438774 - test 1
        1542821438786 - test 2


        But what is more important, it doesn't log field initialization time, because Groovy compiles these 4 blocks to a single one like this:



        static {
        System.out.println(String.format("%d - start", System.currentTimeMillis()));
        Object var10000 = null;
        System.out.println(String.format("%d - object", System.currentTimeMillis()));
        var10000 = null;
        System.out.println(String.format("%d - sales", System.currentTimeMillis()));
        var10000 = null;
        System.out.println(String.format("%d - purchase", System.currentTimeMillis()));
        var10000 = null;
        }


        There is no lag between the 1st and 2nd call, because there is no need to load Groovy classes at this point.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 22 '18 at 8:13









        Opal

        52.3k20104117




        52.3k20104117










        answered Nov 21 '18 at 17:32









        Szymon Stepniak

        16.7k83062




        16.7k83062






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53412363%2fspock-performance-problems%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            404 Error Contact Form 7 ajax form submitting

            How to know if a Active Directory user can login interactively

            TypeError: fit_transform() missing 1 required positional argument: 'X'