Skip to content

Commit a9a492e

Browse files
committed
perf: improve performance with parallel processing, optimized caching, and pre-initialization
1 parent c097df3 commit a9a492e

File tree

5 files changed

+127
-38
lines changed

5 files changed

+127
-38
lines changed

src/Diffy.ts

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@ class Diffy extends BaseDiffy {
4141
this.workspaceService.on(EventType.WORKSPACE_CHANGED, () => {
4242
this.onWorkSpaceChanged();
4343
});
44+
45+
// Pre-initialize AI services to avoid lazy loading delays during commit generation
46+
const provider = this.workspaceService?.getAiServiceProvider();
47+
if (provider === "openai") {
48+
this.getOpenAPIService();
49+
} else if (provider === "gemini") {
50+
this.getGeminiService();
51+
} else {
52+
this.getVsCodeLlmService();
53+
}
54+
55+
// Pre-initialize codebase indexing service if enabled
56+
if (this.workspaceService?.getConfiguration().get("enableCodebaseContext")) {
57+
this.getCodebaseIndexService();
58+
}
59+
4460
this.isEnabled = true;
4561
logger.info("Diffy extension initialized");
4662
}
@@ -125,15 +141,21 @@ class Diffy extends BaseDiffy {
125141
private async prepareDiffWithContext(diff: string): Promise<string> {
126142
try {
127143
const strategy = this.workspaceService?.getCodebaseIndexingStrategy();
128-
const codebaseContext = await this.getCodebaseIndexService().getCodebaseContext();
144+
145+
// Parallel fetch: Get codebase context and analyze diff simultaneously
146+
const [codebaseContext, diffContext] = await Promise.all([
147+
this.getCodebaseIndexService().getCodebaseContext(),
148+
strategy === "structured"
149+
? DiffAnalyzer.getInstance().analyzeGitDiff(diff)
150+
: Promise.resolve(null),
151+
]);
129152

130153
if (codebaseContext) {
131154
logger.info("Adding codebase context to prompt", { strategy });
132155

133156
// If using structured mode, also analyze the diff
134-
if (strategy === "structured") {
157+
if (strategy === "structured" && diffContext) {
135158
const diffAnalyzer = DiffAnalyzer.getInstance();
136-
const diffContext = await diffAnalyzer.analyzeGitDiff(diff);
137159
const compactDiffSummary = diffAnalyzer.formatAsCompact(diffContext);
138160

139161
return `${codebaseContext}\n\nCHANGES SUMMARY:\n${compactDiffSummary}\n\nDIFF:\n${diff}`;
@@ -489,21 +511,29 @@ class Diffy extends BaseDiffy {
489511
if (!repo) {
490512
return;
491513
}
492-
/* Getting the diff of the current git branch. */
514+
515+
/* Getting the diff of the current git branch and codebase context in parallel */
493516
let nameOnly = false;
494-
let diff = await this.gitService?.getDiffAndWarnUser(repo, nameOnly);
517+
const [diff, _] = await Promise.all([
518+
this.gitService?.getDiffAndWarnUser(repo, nameOnly),
519+
// Pre-warm codebase context cache if enabled
520+
this.workspaceService?.getConfiguration().get("enableCodebaseContext")
521+
? this.getCodebaseIndexService().getCodebaseContext()
522+
: Promise.resolve(null),
523+
]);
524+
495525
if (!diff) {
496526
return;
497527
}
498528
if (diff && diff.length >= 2100) {
499529
nameOnly = true;
500-
diff = await this.gitService?.getDiffAndWarnUser(repo, true, nameOnly);
501-
}
502-
if (!diff) {
503-
return;
530+
const newDiff = await this.gitService?.getDiffAndWarnUser(repo, true, nameOnly);
531+
if (!newDiff) {
532+
return;
533+
}
504534
}
505535

506-
// Add codebase context to diff if enabled
536+
// Add codebase context to diff if enabled (uses cached context from parallel fetch)
507537
const enrichedDiff = await this.prepareDiffWithContext(diff);
508538

509539
/* Get AI Service based on provider */

src/service/CacheService.ts

Lines changed: 87 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1+
import * as crypto from "node:crypto";
12
import type { CacheData, CacheResult } from "../@types/extension";
23

4+
interface CacheEntry extends CacheData {
5+
timestamp: number;
6+
ttl: number; // Time to live in milliseconds
7+
}
8+
39
export class CacheService {
410
private static _instance: CacheService;
5-
cache: CacheData[] = [];
11+
private cache: Map<string, CacheEntry> = new Map();
12+
private readonly DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes default TTL
613

714
/**
815
* Returns the singleton instance of the class
@@ -15,39 +22,95 @@ export class CacheService {
1522
return CacheService._instance;
1623
}
1724

18-
/* A method that takes in a entity and data. It then checks if the record exists and if it
19-
doesn't it pushes it to the cache. */
20-
public set = (entity: string, data: string, result: CacheResult): void => {
21-
if (!this.recordExists(entity, data)) {
22-
this.cache.push({
23-
entity: entity,
24-
data: data,
25-
result,
26-
});
25+
/**
26+
* Create a hash key from entity and data for efficient lookups
27+
*/
28+
private createHashKey(entity: string, data: string): string {
29+
// Use SHA-256 hash for long diff content to avoid memory issues with large keys
30+
const hash = crypto.createHash("sha256");
31+
hash.update(`${entity}:${data}`);
32+
return hash.digest("hex");
33+
}
34+
35+
/**
36+
* Clean expired cache entries
37+
*/
38+
private cleanExpired(): void {
39+
const now = Date.now();
40+
for (const [key, entry] of this.cache.entries()) {
41+
if (now - entry.timestamp > entry.ttl) {
42+
this.cache.delete(key);
43+
}
44+
}
45+
}
46+
47+
/**
48+
* Set a cache entry with optional TTL
49+
*/
50+
public set = (entity: string, data: string, result: CacheResult, ttl?: number): void => {
51+
const key = this.createHashKey(entity, data);
52+
53+
// Clean expired entries periodically (every 10th set operation)
54+
if (this.cache.size % 10 === 0) {
55+
this.cleanExpired();
2756
}
57+
58+
this.cache.set(key, {
59+
entity,
60+
data,
61+
result,
62+
timestamp: Date.now(),
63+
ttl: ttl || this.DEFAULT_TTL,
64+
});
2865
};
2966

30-
/* A method that takes in entity and data. It then checks if the record exists and if it
31-
doesn't it pushes it to the cache. */
67+
/**
68+
* Get a cache entry if it exists and hasn't expired
69+
*/
3270
public get = (entity: string, data: string): CacheResult | null => {
33-
const cacheRecord = this.cache.find((x) => {
34-
if (data) {
35-
return x.entity === entity && x.data === data;
36-
}
71+
const key = this.createHashKey(entity, data);
72+
const entry = this.cache.get(key);
3773

38-
return x.entity === entity;
39-
});
74+
if (!entry) {
75+
return null;
76+
}
4077

41-
if (cacheRecord) {
42-
return cacheRecord.result;
78+
// Check if entry has expired
79+
const now = Date.now();
80+
if (now - entry.timestamp > entry.ttl) {
81+
this.cache.delete(key);
82+
return null;
4383
}
4484

45-
return null;
85+
return entry.result;
4686
};
4787

48-
/* It's a method that takes in entity and data . It then checks if the record exists and if it
49-
doesn't it pushes it to the cache. */
88+
/**
89+
* Check if a cache record exists and is valid
90+
*/
5091
public recordExists = (entity: string, data: string): boolean => {
51-
return !!this.get(entity, data);
92+
return this.get(entity, data) !== null;
93+
};
94+
95+
/**
96+
* Clear all cache entries
97+
*/
98+
public clear = (): void => {
99+
this.cache.clear();
100+
};
101+
102+
/**
103+
* Get cache statistics
104+
*/
105+
public getStats = () => {
106+
return {
107+
size: this.cache.size,
108+
entries: Array.from(this.cache.values()).map((entry) => ({
109+
entity: entry.entity,
110+
timestamp: entry.timestamp,
111+
ttl: entry.ttl,
112+
expired: Date.now() - entry.timestamp > entry.ttl,
113+
})),
114+
};
52115
};
53116
}

src/service/GeminiService.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ class GeminiService implements AIService {
191191
increment: 1,
192192
message: "\nCommit message generated.",
193193
});
194-
await new Promise((f) => setTimeout(f, 200));
195194
return response;
196195
}
197196
return undefined;

src/service/OpenAiService.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ class OpenAiService implements AIService {
228228
increment: 1,
229229
message: "\nCommit message generated.",
230230
});
231-
await new Promise((f) => setTimeout(f, 200));
232231
return response;
233232
}
234233
return undefined;

src/service/VsCodeLlmService.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,6 @@ class VsCodeLlmService implements AIService {
318318
message: "\nCommit message generated.",
319319
});
320320

321-
await new Promise((f) => setTimeout(f, 200));
322-
323321
return responseText;
324322
} catch (error: unknown) {
325323
logger.error("VS Code LLM request failed", error);

0 commit comments

Comments
 (0)