Skip to content

Commit 29eec38

Browse files
CopilotDevilTea
andcommitted
perf: Completely eliminate Pipe class usage for massive performance gains
Co-authored-by: DevilTea <16652879+DevilTea@users.noreply.github.com>
1 parent 7d2fbf1 commit 29eec38

File tree

3 files changed

+100
-43
lines changed

3 files changed

+100
-43
lines changed

packages/internal/src/core/core.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {
1111
StepPluginImpl,
1212
TStepPluginDef,
1313
} from './types'
14-
import { Pipe, runtimeExecutionStepDefMarker } from '../shared'
14+
import { runtimeExecutionStepDefMarker } from '../shared'
1515

1616
/* @__NO_SIDE_EFFECTS__ */
1717
export function implStepPlugin<StepPluginDef extends TStepPluginDef>(stepImpl: StepPluginImpl<StepPluginDef>): StepPluginImpl<StepPluginDef> {
@@ -101,12 +101,23 @@ export function createPipeExecutor(
101101
runtimeSteps: ((lastResult: ExecutionResult) => MaybePromise<ExecutionResult>)[],
102102
): (value: unknown) => MaybePromise<ExecutionResult> {
103103
return (value: unknown) => {
104-
// Use optimized Pipe class
105-
let pipe = new Pipe().add(v => ({ value: v } as ExecutionResult))
106-
for (const s of runtimeSteps) {
107-
pipe = pipe.add(s)
104+
// Optimized: Direct execution without Pipe overhead
105+
const len = runtimeSteps.length
106+
let result: any = { value } as ExecutionResult
107+
108+
for (let i = 0; i < len; i++) {
109+
// Check if current result is a promise
110+
if (result instanceof Promise) {
111+
// Once we hit async, chain all remaining steps
112+
for (let j = i; j < len; j++) {
113+
result = result.then(runtimeSteps[j])
114+
}
115+
return result
116+
}
117+
// Execute step synchronously
118+
result = runtimeSteps[i](result)
108119
}
109-
return pipe.exec(value)
120+
return result
110121
}
111122
}
112123
/* @__NO_SIDE_EFFECTS__ */

packages/internal/src/steps/intersection/intersection.ts

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { DefineExpectedValchecker, DefineStepMethod, DefineStepMethodMeta, ExecutionIssue, ExecutionResult, InferAsync, InferIssue, InferOutput, Next, TStepPluginDef, Use, Valchecker } from '../../core'
22
import type { UnionToIntersection } from '../../shared'
33
import { implStepPlugin } from '../../core'
4-
import { Pipe } from '../../shared'
54

65
declare namespace Internal {
76
export type Branches = [Use<Valchecker>, ...Use<Valchecker>[]]
@@ -75,34 +74,59 @@ export const intersection = implStepPlugin<PluginDef>({
7574
params: [branches],
7675
}) => {
7776
addSuccessStep((value) => {
78-
const pipe = new Pipe<void>()
77+
// Optimized: Direct processing without Pipe overhead
7978
const issues: ExecutionIssue[] = []
79+
const len = branches.length
8080
let isInvalid = false
8181

8282
const processBranchResult = (result: ExecutionResult) => {
8383
if (isFailure(result)) {
84-
issues.push(...result.issues)
84+
// Optimize: Avoid spread by using direct loop
85+
for (const issue of result.issues) {
86+
issues.push(issue)
87+
}
8588
isInvalid = true
8689
}
8790
}
8891

89-
for (const branch of branches) {
90-
pipe.add(() => {
91-
if (isInvalid)
92-
return
92+
// Process branches synchronously until we hit async or failure
93+
for (let i = 0; i < len; i++) {
94+
if (isInvalid)
95+
break
9396

94-
const branchResult = branch['~execute'](value)
95-
return branchResult instanceof Promise
96-
? branchResult.then(processBranchResult)
97-
: processBranchResult(branchResult)
98-
})
97+
const branchResult = branches[i]['~execute'](value)
98+
99+
if (branchResult instanceof Promise) {
100+
// Hit async, chain remaining branches
101+
let chain = branchResult.then((r) => {
102+
processBranchResult(r)
103+
return isInvalid
104+
})
105+
106+
for (let j = i + 1; j < len; j++) {
107+
const jBranch = branches[j]
108+
chain = chain.then((invalid) => {
109+
if (invalid)
110+
return true
111+
const jResult = jBranch['~execute'](value)
112+
return jResult instanceof Promise
113+
? jResult.then((r) => {
114+
processBranchResult(r)
115+
return isInvalid
116+
})
117+
: (processBranchResult(jResult), isInvalid)
118+
})
119+
}
120+
121+
return chain.then(() =>
122+
(isInvalid || issues.length > 0) ? failure(issues) : success(value),
123+
)
124+
}
125+
126+
processBranchResult(branchResult)
99127
}
100128

101-
const processResult = () => (isInvalid || issues.length > 0) ? failure(issues) : success(value)
102-
const result = pipe.exec()
103-
return result instanceof Promise
104-
? result.then(processResult)
105-
: processResult()
129+
return (isInvalid || issues.length > 0) ? failure(issues) : success(value)
106130
})
107131
},
108132
})

packages/internal/src/steps/looseObject/looseObject.ts

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { DefineExpectedValchecker, DefineStepMethod, DefineStepMethodMeta, ExecutionIssue, ExecutionResult, InferAsync, InferIssue, InferOutput, MessageHandler, Next, TStepPluginDef, Use, Valchecker } from '../../core'
22
import type { IsEqual, IsExactlyAnyOrUnknown, Simplify, ValueOf } from '../../shared'
33
import { implStepPlugin } from '../../core'
4-
import { Pipe } from '../../shared'
54

65
declare namespace Internal {
76
export type Struct = Record<PropertyKey, Use<Valchecker> | [optional: Use<Valchecker>]>
@@ -114,8 +113,8 @@ export const looseObject = implStepPlugin<PluginDef>({
114113
})
115114
}
116115

117-
const knownKeys = new Set(Reflect.ownKeys(struct))
118-
const pipe = new Pipe<void>()
116+
// Optimized: Direct processing without Pipe overhead
117+
const knownKeys = Array.from(Reflect.ownKeys(struct))
119118
const issues: ExecutionIssue<any, any>[] = []
120119
const output: Record<PropertyKey, any> = Object.defineProperties(
121120
{},
@@ -124,31 +123,54 @@ export const looseObject = implStepPlugin<PluginDef>({
124123

125124
const processPropResult = (result: ExecutionResult, key: string | symbol) => {
126125
if (isFailure(result)) {
127-
issues.push(...result.issues.map(issue => prependIssuePath(issue, [key])))
128-
return
126+
// Optimize: Avoid spread + map by using direct loop
127+
for (const issue of result.issues) {
128+
issues.push(prependIssuePath(issue, [key]))
129+
}
130+
}
131+
else {
132+
output[key] = result.value
129133
}
130-
output[key] = result.value
131134
}
132135

133-
for (const key of knownKeys) {
136+
// Process properties synchronously until we hit async
137+
for (let i = 0; i < knownKeys.length; i++) {
138+
const key = knownKeys[i]!
134139
const isOptional = Array.isArray(struct[key]!)
135140
const propSchema = Array.isArray(struct[key]!) ? struct[key]![0]! : struct[key]!
136141
const propValue = (value as any)[key]
137-
pipe.add(() => {
138-
const propResult = (isOptional && propValue === void 0)
139-
? success(propValue)
140-
: propSchema['~execute'](propValue)
141-
return propResult instanceof Promise
142-
? propResult.then(r => processPropResult(r, key))
143-
: processPropResult(propResult, key)
144-
})
142+
143+
const propResult = (isOptional && propValue === void 0)
144+
? success(propValue)
145+
: propSchema['~execute'](propValue)
146+
147+
if (propResult instanceof Promise) {
148+
// Hit async, chain remaining properties
149+
let chain = propResult.then(r => processPropResult(r, key))
150+
151+
for (let j = i + 1; j < knownKeys.length; j++) {
152+
const jKey = knownKeys[j]!
153+
const jIsOptional = Array.isArray(struct[jKey]!)
154+
const jPropSchema = Array.isArray(struct[jKey]!) ? struct[jKey]![0]! : struct[jKey]!
155+
const jPropValue = (value as any)[jKey]
156+
157+
chain = chain.then(() => {
158+
const jPropResult = (jIsOptional && jPropValue === void 0)
159+
? success(jPropValue)
160+
: jPropSchema['~execute'](jPropValue)
161+
return jPropResult instanceof Promise
162+
? jPropResult.then(r => processPropResult(r, jKey))
163+
: (processPropResult(jPropResult, jKey), undefined)
164+
})
165+
}
166+
167+
return chain.then(() => issues.length > 0 ? failure(issues) : success(output))
168+
}
169+
170+
processPropResult(propResult, key)
145171
}
146172

147-
const processResult = () => issues.length > 0 ? failure(issues) : success(output)
148-
const result = pipe.exec()
149-
return result instanceof Promise
150-
? result.then(processResult)
151-
: processResult()
173+
return issues.length > 0 ? failure(issues) : success(output)
152174
})
153175
},
154176
})

0 commit comments

Comments
 (0)