Skip to content

Commit 15f2a14

Browse files
authored
Check memory usage only if jedi is used and reduce frequency of these checks (#1510)
Fixes #1277 * Use settimeout instead of setinterval (the requests were getting piled up with setinterval). * I've reduced the frequency of checks from 2 seconds to 15 seconds. * Check memory usage only if jedi is used>
1 parent bffcbc1 commit 15f2a14

File tree

2 files changed

+41
-18
lines changed

2 files changed

+41
-18
lines changed

news/2 Fixes/1277.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Reduce the frequency within which the memory usage of the language server is checked, also ensure memory usage is not checked unless language server functionality is used.

src/client/providers/jediProxy.ts

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { ChildProcess } from 'child_process';
66
import * as fs from 'fs-extra';
77
import * as path from 'path';
88
import * as pidusage from 'pidusage';
9-
import { setInterval } from 'timers';
109
import { CancellationToken, CancellationTokenSource, CompletionItemKind, Disposable, SymbolKind, Uri } from 'vscode';
1110
import { PythonSettings } from '../common/configSettings';
1211
import { debounce, swallowExceptions } from '../common/decorators';
@@ -147,6 +146,8 @@ export class JediProxy implements Disposable {
147146
private logger: ILogger;
148147
private ignoreJediMemoryFootprint: boolean = false;
149148
private pidUsageFailures = { timer: new StopWatch(), counter: 0 };
149+
private lastCmdIdProcessed?: number;
150+
private lastCmdIdProcessedForPidUsage?: number;
150151

151152
public constructor(private extensionRootDir: string, workspacePath: string, private serviceContainer: IServiceContainer) {
152153
this.workspacePath = workspacePath;
@@ -157,12 +158,7 @@ export class JediProxy implements Disposable {
157158
this.initialized = createDeferred<void>();
158159
this.startLanguageServer().then(() => this.initialized.resolve()).ignoreErrors();
159160

160-
// Check memory footprint periodically. Do not check on every request due to
161-
// the performance impact. See https://github.com/soyuka/pidusage - on Windows
162-
// it is using wmic which means spawning cmd.exe process on every request.
163-
if (this.shouldCheckJediMemoryFootprint()) {
164-
setInterval(() => this.checkJediMemoryFootprint(), 2000);
165-
}
161+
this.checkJediMemoryFootprint().ignoreErrors();
166162
}
167163

168164
private static getProperty<T>(o: object, name: string): T {
@@ -220,32 +216,58 @@ export class JediProxy implements Disposable {
220216
if (this.ignoreJediMemoryFootprint || this.pythonSettings.jediMemoryLimit === -1) {
221217
return false;
222218
}
219+
if (this.lastCmdIdProcessedForPidUsage && this.lastCmdIdProcessed &&
220+
this.lastCmdIdProcessedForPidUsage === this.lastCmdIdProcessed) {
221+
// If no more commands were processed since the last time,
222+
// then there's no need to check again.
223+
return false;
224+
}
223225
return true;
224226
}
225-
private checkJediMemoryFootprint() {
227+
private async checkJediMemoryFootprint() {
228+
// Check memory footprint periodically. Do not check on every request due to
229+
// the performance impact. See https://github.com/soyuka/pidusage - on Windows
230+
// it is using wmic which means spawning cmd.exe process on every request.
231+
if (this.pythonSettings.jediMemoryLimit === -1) {
232+
return;
233+
}
234+
235+
await this.checkJediMemoryFootprintImpl();
236+
setTimeout(() => this.checkJediMemoryFootprint(), 15 * 1000);
237+
}
238+
private async checkJediMemoryFootprintImpl(): Promise<void> {
226239
if (!this.proc || this.proc.killed) {
227240
return;
228241
}
229242
if (!this.shouldCheckJediMemoryFootprint()) {
230243
return;
231244
}
245+
this.lastCmdIdProcessedForPidUsage = this.lastCmdIdProcessed;
246+
247+
// Do not run pidusage over and over, wait for it to finish.
248+
const deferred = createDeferred<void>();
232249
pidusage.stat(this.proc.pid, async (err, result) => {
233250
if (err) {
234251
this.pidUsageFailures.counter += 1;
235-
// If this function fails 5 times in the last 30 seconds, lets not try ever again.
236-
if (this.pidUsageFailures.timer.elapsedTime > 30 * 1000) {
237-
this.ignoreJediMemoryFootprint = this.pidUsageFailures.counter > 5;
252+
// If this function fails 2 times in the last 60 seconds, lets not try ever again.
253+
if (this.pidUsageFailures.timer.elapsedTime > 60 * 1000) {
254+
this.ignoreJediMemoryFootprint = this.pidUsageFailures.counter > 2;
238255
this.pidUsageFailures.counter = 0;
239256
this.pidUsageFailures.timer.reset();
240257
}
241-
return console.error('Python Extension: (pidusage)', err);
242-
}
243-
const limit = Math.min(Math.max(this.pythonSettings.jediMemoryLimit, 1024), 8192);
244-
if (result && result.memory > limit * 1024 * 1024) {
245-
this.logger.logWarning(`IntelliSense process memory consumption exceeded limit of ${limit} MB and process will be restarted.\nThe limit is controlled by the 'python.jediMemoryLimit' setting.`);
246-
await this.restartLanguageServer();
258+
console.error('Python Extension: (pidusage)', err);
259+
} else {
260+
const limit = Math.min(Math.max(this.pythonSettings.jediMemoryLimit, 1024), 8192);
261+
if (result && result.memory > limit * 1024 * 1024) {
262+
this.logger.logWarning(`IntelliSense process memory consumption exceeded limit of ${limit} MB and process will be restarted.\nThe limit is controlled by the 'python.jediMemoryLimit' setting.`);
263+
await this.restartLanguageServer();
264+
}
247265
}
266+
267+
deferred.resolve();
248268
});
269+
270+
return deferred.promise;
249271
}
250272

251273
@swallowExceptions('JediProxy')
@@ -374,7 +396,7 @@ export class JediProxy implements Disposable {
374396
if (cmd === null) {
375397
return;
376398
}
377-
399+
this.lastCmdIdProcessed = cmd.id;
378400
if (JediProxy.getProperty<object>(response, 'arguments')) {
379401
this.commandQueue.splice(this.commandQueue.indexOf(cmd.id), 1);
380402
return;

0 commit comments

Comments
 (0)