Skip to content

Commit f0df4df

Browse files
committed
internal/core/eval: performance: unify an arc only once per node
This can avoid an exponential blowup. Note that this still does redundant work. Removing this redundancy will significantly reduce the running time of tests and even improve the running time of the benchmarks added in this test. Change-Id: Icbb19e02720e9f904f7326c92c9f404c817a4792 Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7163 Reviewed-by: CUE cueckoo <[email protected]> Reviewed-by: Marcel van Lohuizen <[email protected]>
1 parent da69dc0 commit f0df4df

File tree

7 files changed

+369
-7
lines changed

7 files changed

+369
-7
lines changed

cue/testdata/benchmarks/bench_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2020 CUE Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package benchmarks
16+
17+
import (
18+
"io/ioutil"
19+
"path/filepath"
20+
"testing"
21+
22+
"cuelang.org/go/cue"
23+
"cuelang.org/go/internal/cuetxtar"
24+
"github.com/rogpeppe/go-internal/txtar"
25+
)
26+
27+
func Benchmark(b *testing.B) {
28+
files, err := ioutil.ReadDir(".")
29+
if err != nil {
30+
b.Fatal(err)
31+
}
32+
33+
for _, fi := range files {
34+
name := fi.Name()
35+
if fi.IsDir() || filepath.Ext(name) != ".txtar" {
36+
continue
37+
}
38+
39+
a, err := txtar.ParseFile(name)
40+
if err != nil {
41+
b.Fatal(err)
42+
}
43+
44+
inst := cue.Build(cuetxtar.Load(a, "/cuetest"))[0]
45+
if inst.Err != nil {
46+
b.Fatal(inst.Err)
47+
}
48+
49+
b.Run(name, func(b *testing.B) {
50+
for i := 0; i < b.N; i++ {
51+
inst := cue.Build(cuetxtar.Load(a, "/cuetest"))[0]
52+
if inst.Err != nil {
53+
b.Fatal(inst.Err)
54+
}
55+
56+
inst.Value().Validate()
57+
}
58+
})
59+
}
60+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
-- in.cue --
2+
package bench1
3+
4+
#Value: {type: "float"} | {type: "string"}
5+
6+
foo: {type: "string"}
7+
foo: #Value
8+
foo: #Value
9+
foo: #Value
10+
foo: #Value
11+
foo: #Value
12+
foo: #Value
13+
foo: #Value
14+
foo: #Value
15+
foo: #Value
16+
foo: #Value
17+
foo: #Value
18+
foo: #Value
19+
foo: #Value
20+
foo: #Value
21+
foo: #Value
22+
-- out/eval --
23+
(struct){
24+
#Value: (struct){ |((#struct){
25+
type: (string){ "float" }
26+
}, (#struct){
27+
type: (string){ "string" }
28+
}) }
29+
foo: (#struct){
30+
type: (string){ "string" }
31+
}
32+
}
33+
-- out/compile --
34+
--- in.cue
35+
{
36+
#Value: ({
37+
type: "float"
38+
}|{
39+
type: "string"
40+
})
41+
foo: {
42+
type: "string"
43+
}
44+
foo: 〈0;#Value〉
45+
foo: 〈0;#Value〉
46+
foo: 〈0;#Value〉
47+
foo: 〈0;#Value〉
48+
foo: 〈0;#Value〉
49+
foo: 〈0;#Value〉
50+
foo: 〈0;#Value〉
51+
foo: 〈0;#Value〉
52+
foo: 〈0;#Value〉
53+
foo: 〈0;#Value〉
54+
foo: 〈0;#Value〉
55+
foo: 〈0;#Value〉
56+
foo: 〈0;#Value〉
57+
foo: 〈0;#Value〉
58+
foo: 〈0;#Value〉
59+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
-- in.cue --
2+
package lpcorpus
3+
4+
#Value: {type: "int"} | {type: "float"} | {type: "string"}
5+
6+
foo: [{type: "float"}] & [...#Value]
7+
foo: [{type: "float"}] & [...#Value]
8+
foo: [{type: "float"}] & [...#Value]
9+
foo: [{type: "float"}] & [...#Value]
10+
foo: [{type: "float"}] & [...#Value]
11+
foo: [{type: "string"}] & [...#Value]
12+
foo: [{type: "string"}] & [...#Value]
13+
foo: [{type: "string"}] & [...#Value]
14+
foo: [{type: "float"}] & [...#Value]
15+
foo: [{type: "float"}] & [...#Value]
16+
-- out/eval --
17+
Errors:
18+
foo.0.type: incompatible values "string" and "float"
19+
20+
Result:
21+
(_|_){
22+
// [eval]
23+
#Value: (struct){ |((#struct){
24+
type: (string){ "int" }
25+
}, (#struct){
26+
type: (string){ "float" }
27+
}, (#struct){
28+
type: (string){ "string" }
29+
}) }
30+
foo: (_|_){
31+
// [eval]
32+
0: (_|_){
33+
// [eval]
34+
type: (_|_){
35+
// [eval] foo.0.type: incompatible values "string" and "float"
36+
}
37+
}
38+
}
39+
}
40+
-- out/compile --
41+
--- in.cue
42+
{
43+
#Value: ({
44+
type: "int"
45+
}|{
46+
type: "float"
47+
}|{
48+
type: "string"
49+
})
50+
foo: ([
51+
{
52+
type: "float"
53+
},
54+
] & [
55+
...〈0;#Value〉,
56+
])
57+
foo: ([
58+
{
59+
type: "float"
60+
},
61+
] & [
62+
...〈0;#Value〉,
63+
])
64+
foo: ([
65+
{
66+
type: "float"
67+
},
68+
] & [
69+
...〈0;#Value〉,
70+
])
71+
foo: ([
72+
{
73+
type: "float"
74+
},
75+
] & [
76+
...〈0;#Value〉,
77+
])
78+
foo: ([
79+
{
80+
type: "float"
81+
},
82+
] & [
83+
...〈0;#Value〉,
84+
])
85+
foo: ([
86+
{
87+
type: "string"
88+
},
89+
] & [
90+
...〈0;#Value〉,
91+
])
92+
foo: ([
93+
{
94+
type: "string"
95+
},
96+
] & [
97+
...〈0;#Value〉,
98+
])
99+
foo: ([
100+
{
101+
type: "string"
102+
},
103+
] & [
104+
...〈0;#Value〉,
105+
])
106+
foo: ([
107+
{
108+
type: "float"
109+
},
110+
] & [
111+
...〈0;#Value〉,
112+
])
113+
foo: ([
114+
{
115+
type: "float"
116+
},
117+
] & [
118+
...〈0;#Value〉,
119+
])
120+
}

cue/testdata/definitions/embed.txtar

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,27 @@ reclose2: {
4646
z: d: 3 // allow this
4747
}
4848

49-
49+
reclose3: {
50+
#Step: (#A | #B) & {
51+
#Common
52+
}
53+
#Common: {
54+
Name: string
55+
}
56+
#A: {
57+
#Common
58+
Something: int
59+
}
60+
#B: {
61+
#Common
62+
Else: int
63+
}
64+
x: #Step
65+
x: #A & {
66+
Name: "a"
67+
Something: 4
68+
}
69+
}
5070

5171
-- out/eval --
5272
Errors:
@@ -116,6 +136,30 @@ Result:
116136
d: (int){ 3 }
117137
}
118138
}
139+
reclose3: (struct){
140+
#Step: (struct){ |((#struct){
141+
Name: (string){ string }
142+
Something: (int){ int }
143+
}, (#struct){
144+
Name: (string){ string }
145+
Else: (int){ int }
146+
}) }
147+
#Common: (#struct){
148+
Name: (string){ string }
149+
}
150+
#A: (#struct){
151+
Name: (string){ string }
152+
Something: (int){ int }
153+
}
154+
#B: (#struct){
155+
Name: (string){ string }
156+
Else: (int){ int }
157+
}
158+
x: (#struct){
159+
Name: (string){ "a" }
160+
Something: (int){ 4 }
161+
}
162+
}
119163
}
120164
-- out/compile --
121165
--- in.cue
@@ -170,4 +214,25 @@ Result:
170214
d: 3
171215
}
172216
}
217+
reclose3: {
218+
#Step: ((〈0;#A〉|〈0;#B〉) & {
219+
〈1;#Common〉
220+
})
221+
#Common: {
222+
Name: string
223+
}
224+
#A: {
225+
〈1;#Common〉
226+
Something: int
227+
}
228+
#B: {
229+
〈1;#Common〉
230+
Else: int
231+
}
232+
x: 〈0;#Step〉
233+
x: (〈0;#A〉 & {
234+
Name: "a"
235+
Something: 4
236+
})
237+
}
173238
}

internal/core/adt/composite.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -532,15 +532,12 @@ func (v *Vertex) Source() ast.Node { return nil }
532532
// AddConjunct adds the given Conjuncts to v if it doesn't already exist.
533533
func (v *Vertex) AddConjunct(c Conjunct) *Bottom {
534534
if v.Value != nil {
535+
// TODO: investigate why this happens at all. Removing it seems to
536+
// change the order of fields in some cases.
537+
//
535538
// This is likely a bug in the evaluator and should not happen.
536539
return &Bottom{Err: errors.Newf(token.NoPos, "cannot add conjunct")}
537540
}
538-
for _, x := range v.Conjuncts {
539-
if x == c {
540-
return nil
541-
}
542-
}
543-
544541
v.Conjuncts = append(v.Conjuncts, c)
545542
return nil
546543
}

0 commit comments

Comments
 (0)