Skip to content

Immediately compiled SpEL Expression slows down on each use #33837

@Fradantim

Description

@Fradantim

Hello There!

We use lots of SpEL Expressions to manipulate objects and create new instances of objects (mostly strings).

In a recent poc testing performance of differents alternatives to this use case we found out immediately compiled SpelExpression starts quite fast, but after a short time (or some millons of ops) it's throughput halves.

Using
Java:

java version "21" 2023-09-19
Java(TM) SE Runtime Environment Oracle GraalVM 21+35.1 (build 21+35-jvmci-23.1-b15)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 21+35.1 (build 21+35-jvmci-23.1-b15, mixed mode, sharing)

Spring

org.springframework.boot:spring-boot-starter-parent:3.3.4 (org.springframework:spring-expression:6.1.13)
public static void main(String... s) {
  Map<String, Object> root = Map.of("foo", "bar");
  Expression expression = new SpelExpressionParser(new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null)).parseExpression("[foo].substring(0,1)");
  AtomicInteger expressionOps = new AtomicInteger();
  int loopCount = 0;
  while (loopCount < 120) {
    loopCount++;
    expressionOps.set(0);
    try {
      CompletableFuture.supplyAsync(() -> {
        while (true) {
          expression.getValue(root);
          expressionOps.incrementAndGet();
        }
      }).get(1, TimeUnit.SECONDS);
    } catch (TimeoutException | ExecutionException | InterruptedException e) {
      // no op
    }
    System.out.println(loopCount + "\t" + expressionOps + " ops/sec");
  }
}

(I know this may not be the best way to execute a performance test)

result
1	106468809 ops/sec
2	76858700 ops/sec
3	58440833 ops/sec
4	63723883 ops/sec
5	63029349 ops/sec
6	61847942 ops/sec
7	62372654 ops/sec
8	61530194 ops/sec
9	62077340 ops/sec
10	62611521 ops/sec
11	63168501 ops/sec
12	61686067 ops/sec
13	61269262 ops/sec
14	62456026 ops/sec
15	67376736 ops/sec
16	62491016 ops/sec
17	61248502 ops/sec
18	58272199 ops/sec
19	61588422 ops/sec
20	62494339 ops/sec
21	63933284 ops/sec
22	60169862 ops/sec
23	60263788 ops/sec
24	58811820 ops/sec
25	62337873 ops/sec
26	64092402 ops/sec
27	64226228 ops/sec
28	63938694 ops/sec
29	62280862 ops/sec
30	61883910 ops/sec
31	59772020 ops/sec
32	55845966 ops/sec
33	58088455 ops/sec
34	60313176 ops/sec
35	61702904 ops/sec
36	60058085 ops/sec
37	58084405 ops/sec
38	59141274 ops/sec
39	57150345 ops/sec
40	59340823 ops/sec
41	59350710 ops/sec
42	59292279 ops/sec
43	59118264 ops/sec
44	54717859 ops/sec
45	56027505 ops/sec
46	56884413 ops/sec
47	56753541 ops/sec
48	54790069 ops/sec
49	56914703 ops/sec
50	58801666 ops/sec
51	61203990 ops/sec
52	59835119 ops/sec
53	58705683 ops/sec
54	57631493 ops/sec
55	55475370 ops/sec
56	56066452 ops/sec
57	59352852 ops/sec
58	56569769 ops/sec
59	55238780 ops/sec
60	56775104 ops/sec
61	62619808 ops/sec
62	63180309 ops/sec
63	62635372 ops/sec
64	62340839 ops/sec
65	63069140 ops/sec
66	63534674 ops/sec
67	58739326 ops/sec
68	57709071 ops/sec
69	52988673 ops/sec
70	56862452 ops/sec
71	57835487 ops/sec
72	59130428 ops/sec
73	63811116 ops/sec
74	64912248 ops/sec
75	65622490 ops/sec
76	63288306 ops/sec
77	63424435 ops/sec
78	64440044 ops/sec
79	61442975 ops/sec
80	61505119 ops/sec
81	56322460 ops/sec
82	53251012 ops/sec
83	56405411 ops/sec
84	56411554 ops/sec
85	55714282 ops/sec
86	59683349 ops/sec
87	59343053 ops/sec
88	60412439 ops/sec
89	60083218 ops/sec
90	58158879 ops/sec
91	60024897 ops/sec
92	59011061 ops/sec
93	59899315 ops/sec
94	56477961 ops/sec
95	52951155 ops/sec
96	58656927 ops/sec
97	57973760 ops/sec
98	58851680 ops/sec
99	58094087 ops/sec
100	59485404 ops/sec
101	58373482 ops/sec
102	57623260 ops/sec
103	56687019 ops/sec
104	59189517 ops/sec
105	61541489 ops/sec
106	56009505 ops/sec
107	54406023 ops/sec
108	55683797 ops/sec
109	57794844 ops/sec
110	56801866 ops/sec
111	58971439 ops/sec
112	61844510 ops/sec
113	62648223 ops/sec
114	61866733 ops/sec
115	64274086 ops/sec
116	63203469 ops/sec
117	64850860 ops/sec
118	63431436 ops/sec
119	54036480 ops/sec
120	56601604 ops/sec
---
config:
    themeVariables:
        xyChart:
            plotColorPalette: "#ff0000"
---
xychart-beta
    title "Throughput of the first 4 seconds"
    y-axis "operations" 0 --> 107000000
    x-axis "seconds" [1, 2, 3, 4, 5, 6, 7, 8]
    line [106468809, 76858700, 58440833, 63723883, 63029349, 61847942, 62372654, 61530194]
Loading

Is this not an intended use case?

Can I do something to maintain that initial throughput?

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)status: invalidAn issue that we don't feel is valid

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions