# PSI Performance

> For the complete documentation index, see [llms.txt](https://plugins.jetbrains.com/docs/intellij/llms.txt).

See also [Avoiding UI Freezes](threading-model.html#avoiding-ui-freezes) and [Improving Indexing Performance](indexing-and-psi-stubs.html#improving-indexing-performance).

> **Tip:**
> [IDE Perf](https://plugins.jetbrains.com/plugin/15104-ide-perf) plugin provides on-the-fly performance diagnostic tools, including a dedicated view for [CachedValue](#cache-results-of-heavy-computations) metrics.

## Avoid Repeated Calls of PSI Getters

PSI has a lot of time-space compromises. There are tons of PSI elements in IDE memory, so the IntelliJ Platform and language plugins strive to keep them as compact as possible, storing very little data inside. As a result, many things are recomputed on every call of getter methods on `PsiElement` and its subclasses. They are not stored inside the fields of `PsiElement`s.

For example, consider the following Java expression:

```JAVA
String.format("Hello, %s!", name);
```

This is its (simplified) PSI:

```TEXT
PsiMethodCallExpression:String.format("Hello, %s!", name)
  PsiReferenceExpression:String.format
  PsiExpressionList
    PsiJavaToken:LPARENTH
    PsiLiteralExpression:"Hello, %s!"
    PsiJavaToken:COMMA
    PsiReferenceExpression:name
    PsiJavaToken:RPARENTH
```

Assume that plugin code receives `PsiMethodCallExpression call` and needs to get the first and last expression passed as arguments to this method call. This can be done with `methodCall.getArgumentList().getExpressions()`, which does the following:

1. `getArgumentList()` traverses the linked list of children, looking for an element of a proper type (in this case – `PsiExpressionList`)

2. `getExpressions()` traverses the children to find all the expressions (elements of `PsiExpression`), then allocates an array of the target size, and then traverses the children again to fill in this array.

These `get*()` methods are not simple getters that return a readily available value – keep this in mind when working with PSI.

As a rule, avoid calling the same method twice, one after another. Instead, it's better to store the result in a local variable.

Find the first and last expression argument passed to a method call could be implemented as follows:

Inefficient:

```JAVA
PsiExpression first = call.getArgumentList().getExpressions()[0];
int lastIndex = call.getArgumentList().getExpressionCount() - 1;
PsiExpression last = call.getArgumentList().getExpressions()[lastIndex];
```

Optimized:

```JAVA
PsiExpression[] expressions = call.getArgumentList().getExpressions();
PsiExpression first = expressions[0];
PsiExpression last = expressions[expressions.length - 1];
```

##  Avoid Expensive Methods of `PsiElement` 

Avoid `PsiElement`'s methods, which are expensive with deep trees.

`getText()` traverses the whole tree under the given element and concatenates strings, consider using `textMatches()` instead.

`getTextRange()`, `getContainingFile()`, and `getProject()` traverse the tree up to the file, which can be long in very nested trees. If only the PSI element length is needed, use `getTextLength()`.

`getContainingFile()` and `getProject()` often can be computed once per task and then stored in fields or passed via parameters.

Additionally, methods such as `getText()`, `getNode()`, or `getTextRange()` require the AST, and accessing it can be an expensive operation, as explained in the next section.

## Avoid Using Many PSI Trees/Documents

Avoid loading too many parsed trees or documents into memory at the same time. Ideally, only AST nodes from files open in the editor should be present in the memory. Everything else, even if it's necessary for resolve/highlighting purposes, can be accessed via PSI interfaces, but its implementations should [use stubs](stub-indexes.html) underneath, which are less CPU- and memory-expensive.

If stubs do not suit the case well (e.g., the required information is large and/or very rarely needed, or the plugin is developed for a language whose PSI it does not control), create a [custom index or gist](indexing-and-psi-stubs.html).

To ensure that AST is not loaded accidentally, use [AstLoadingFilter](https://github.com/JetBrains/intellij-community/tree/idea/263.7307.200/platform/core-api/src/com/intellij/util/AstLoadingFilter.java) in production and `PsiManagerEx.setAssertOnFileLoadingFilter()` in tests.

The same applies to documents: only the ones opened in editors should be loaded. Usually, document contents should not be needed (as most information can be retrieved from PSI). If documents are nevertheless needed, consider saving the information to provide in a [custom index or gist](indexing-and-psi-stubs.html) to get it more cheaply later. If documents are still needed, then at least ensure they are loaded one by one and not held on strong references to let GC free the memory as quickly as possible.

## Cache Results of Heavy Computations

Method calls such as `PsiElement.getReference()` (and `getReferences()`), `PsiReference.resolve()` (and `multiResolve()` and other equivalents) or computation of expression types, type inference results, control flow graphs, etc. can be expensive. To avoid paying this cost several times, the result of such computation can be cached and reused. Usually, [CachedValue](https://github.com/JetBrains/intellij-community/tree/idea/263.7307.200/platform/core-api/src/com/intellij/psi/util/CachedValue.java) created with [CachedValuesManager](https://github.com/JetBrains/intellij-community/tree/idea/263.7307.200/platform/core-api/src/com/intellij/psi/util/CachedValuesManager.java) works well for this purpose.

If the cached information depends only on a subtree of the current PSI element (and nothing else: no resolve results or other files), cache it in a field in the `PsiElement` implementation and drop the cache in an override of `ASTDelegatePsiElement.subtreeChanged()`.

###  Using `ProjectRootManager` as a Dependency 

The platform no longer increments root changes modification tracker on finish of [dumb mode](indexing-and-psi-stubs.html#dumb-mode). If cached values use [ProjectRootManager](https://github.com/JetBrains/intellij-community/tree/idea/263.7307.200/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectRootManager.java) as a dependency (without [PsiModificationTracker](https://github.com/JetBrains/intellij-community/tree/idea/263.7307.200/platform/core-api/src/com/intellij/psi/util/PsiModificationTracker.java)) and at the same time depend on [indexes](indexing-and-psi-stubs.html), a dependency on [DumbService](https://github.com/JetBrains/intellij-community/tree/idea/263.7307.200/platform/core-api/src/com/intellij/openapi/project/DumbService.kt) must be added.

