Repository: cucumber/cucumber-jvm

Issue #2035: Single step expression is compiled into regex more than once (thus it makes test execution slower)
Created: 2020-06-26T20:46:24Z
It seems that all step expressions are re-compiled into regexes before each scenario.
Steps to reproduce the behavior:

Given I have feature file like:

Feature: Whatever

Scenario: S1
  Given foo

Scenario: S2
  Given foo


and I implement foo step e.g.

@Given("foo")
public void foo() {}


When I execute the feature file
Then io.cucumber.core.stepexpression.StepExpressionFactory.createExpression method is executed twice with "foo" expression (because there are two scenarios)
therefore "foo" is compiled into regex twice

Expected behavior
"foo" is compiled only once.
Context & Motivation
I noticed that if I remove all bodies of my steps from sources it still takes considerable amount of time to run my tests (e.g., 2.2 seconds for 186 scenarios). Using Java Flight Recorder I collected some profiling data and it seems that a lot of time is being spent in io.cucumber.core.stepexpression.StepExpressionFactory.createExpression (47% in my case according to flame graph - see below). When I examined that method in debugger I noticed that it's being called more than once with same expressionString parameter.
Screenshots

Your Environment

Versions used 6.1.2
Linux, Gradle, Kotlin

Duplicate issue #2504: Performance problem with CachingGlue / ExpressionFactory
Created: 2022-03-24T09:50:51Z
👓 What did you see?
We execute cucumber scenarios with a high number of threads (> 150). We had massive problems with the tests starting slowly and needing a lot of initial CPU. Profiling the code I found the main bottleneck to be the creation of expressions in io.cucumber.cucumberexpressions.ExpressionFactory#createExpression which is called (via some other methods) by io.cucumber.core.runner.CachingGlue#prepareGlue.
For every thread all glue code classes are parsed and step definitions are created
✅ What did you expect to see?
As the glue code is the same for all scenarios I'd expect it to be parsed once beforehand. I wrote a custom implementation with copies of some of the involved classes which (very hackily) saves the created step definitions of ExpressionFactory in a static map. This reduces the startup time of the tests on our pipeline drastically.
📦 Which version are you using?
7.0.0
🔬 How could we reproduce it?
Start 150 scenarios or more in parallel. Or just two - the basic problem is always there, it's just more apparent with more threads.
🤔 Anything else?
We have more than 5000 step definitions using method annotations and use a lot of custom parameter types which makes parsing them quite heavy.

Closing comment (2022-03-24T11:42:53Z): As the glue code is the same for all scenarios I'd expect it to be parsed once beforehand.

Unfortunately this isn't true. Due to some unfortunate design choices cucumber-java8 must define step definitions and parameter types at runtime. While in practice they're always the same, we can't quite ensure this.
This particular problem would be resolved by #2279. After that step definitions would only have to be compiled once per language and can be made immutable.
Closing as a duplicate of #2035.