Skip to content

Commit

Permalink
Add performance benchmarks for Vendr's codebase
Browse files Browse the repository at this point in the history
  • Loading branch information
ryan-haskell committed Feb 28, 2023
1 parent 223b10d commit fcaa85d
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 32 deletions.
92 changes: 74 additions & 18 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- [Convert HTML to Elm](#convert-html-to-elm)
- 📊 [Performance Table](#performance-table)
- 💖 [Thank you, Elm community](#thank-you-elm-community)
- 🛠️ [Want to contribute?](#want-to-contribute)

## __Features__

Expand Down Expand Up @@ -100,38 +101,55 @@ To help you convert HTML snippets to Elm code and help newcomers learn the synta

Elm's [editor plugins repo](https://github.com/elm/editor-plugins) recommends doing performance profiling to help others learn how different editors implement features, and also to help try to think of ways to bring down costs.

This VS code plugin was specifically designed to have __near-zero memory overhead [¹](#-ram-overhead)__, and to __avoid in-memory indexing__ that cache your codebase before invoking features. For this reason, it's been very effective at [Vendr](https://vendr.com), even though the frontend codebase is __over 400k lines__ of Elm code.
This VS code plugin was specifically designed to have __near-zero memory overhead [¹](#low-memory-overhead)__, and to __avoid in-memory indexing__ that cache your codebase before invoking features. For this reason, it's been very effective at [Vendr](https://vendr.com), even though the frontend codebase is __over 400k lines__ of Elm code.

---
### __Massive Elm projects__ (578k lines of Elm)

These benchmarks were taken on a __MacBook Pro (2020) [²](#macbook-specs)__ testing this editor plugin against [Vendr](https://vendr.com)'s codebase, which has __578k lines of Elm code__ across 3,384 Elm files.

Feature | Average Speed | Constant RAM Overhead | Cumulative CPU Costs
:------ | :------------ | :-------------------- | :-------------------
__Format on save__ | <200ms | _None_ | On command
__Error highlighting__| <500ms | _None_ | On file open and save
__Jump-to-definition__ | <600ms | _None_ | On file open and save
__Offline package docs__ | <400ms | _None_ | On command
__Module import autocomplete__ | <100ms | _None_ | On key stroke
__Convert HTML to Elm__ | <100ms | _None_ | On command

### `rtfeldman/elm-spa-example` (4K LOC, 34 files)

These benchmarks were taken on a __Windows PC [²](#-pc-specs)__ testing this plugin against [rtfeldman/elm-spa-example](https://github.com/rtfeldman/elm-spa-example) repository, which has 3.8k lines of Elm code across 34 files.
### __Medium-size projects__ (3.8k lines of Elm)

Feature | Average Speed | Constant RAM Overhead | Cumulative CPU Costs | Battery Implications
:------ | :------------ | :-------------------- | :------------------- | :-------------------
__Format on save__ | <500ms | _None_ | On command | notable
__Error highlighting__| <500ms | _None_ | On file open and save | minimal
__Jump-to-definition__ | <150ms | _None_ | On file open and save | notable
__Offline package docs__ | <100ms | _None_ | On command | minimal
__Module import autocomplete__ | <100ms | _None_ | On key stroke | minimal
__Convert HTML to Elm__ | <100ms | _None_ | On command | minimal
These benchmarks were taken on a __Windows PC [³](#pc-specs)__ testing this editor plugin against [rtfeldman/elm-spa-example](https://github.com/rtfeldman/elm-spa-example) repository, which has __3.8k lines of Elm code__ across 34 files.

Feature | Average Speed | Constant RAM Overhead | Cumulative CPU Costs
:------ | :------------ | :-------------------- | :-------------------
__Format on save__ | <300ms | _None_ | On command
__Error highlighting__| <500ms | _None_ | On file open and save
__Jump-to-definition__ | <150ms | _None_ | On file open and save
__Offline package docs__ | <100ms | _None_ | On command
__Module import autocomplete__ | <100ms | _None_ | On key stroke
__Convert HTML to Elm__ | <100ms | _None_ | On command

#### __¹. RAM overhead__
#### __Low memory overhead__

The only in-memory overhead from this plugin comes from caching the contents of your `elm.json` files within the current workspace, and any `docs.json` files for packages that you are using.

For example, if your project is using `elm/[email protected]`, the contents of `$ELM_HOME/0.19.1/packages/elm/http/2.0.0/docs.json` would be cached in working RAM to improve performance for the [Offline package docs](#offline-package-docs), [Module import autocomplete](#module-import-autocomplete), and [Jump-to-definition](#jump-to-definition) features.
This means a __tiny project with 10 lines of Elm code__ and a __huge project with 500k+ lines of Elm code__ would have __the same RAM overhead__– assuming they both had the same Elm package dependencies.

This means a __tiny project with 10 lines of Elm code__ and a __huge project with 500k+ lines of Elm code__, would have __the same RAM overhead__, assuming they had the same Elm package dependencies!
#### __MacBook Specs__

#### __². PC Specs__
The MacBook Pro used for benchmarking had the following specifications:
- __Model__: MacBook Pro (13-inch 2020)
- __OS__: macOS Ventura 13.2.1
- __Processor__: 1.7 GHz Quad-Core Intel Core i7
- __Memory__: 16GB

The Windows PC has the following specifications:
#### __PC Specs__

The Windows PC used for benchmarking has the following specifications:
- __OS__: Windows 11 Home 64-bit
- __Processor__: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz (16 CPUs), ~2.4GHz
- __Memory:__ 16GB RAM
- __Memory:__ 16GB

## __Thank you, Elm community!__

Expand Down Expand Up @@ -175,3 +193,41 @@ Thank you [Rupert](https://github.com/rupertlssmith) and [pwentz](https://github
Jim provided the HTML parser that powers the __HTML to Elm__ feature. The [jims/html-parser](https://github.com/jims/html-parser) package made it easy for to add the feature to help lower the learning curve for newcomers to Elm.

Thank you, Jim! Your Elm package rocks!


## __Want to contribute?__

Great! This project is focused on the following high-level goals:

1. __Every feature should work reliably.__

Any developer using Mac, Windows, or Linux should be able to download this plugin, and have it reliably work for any valid Elm project.

2. __Create a fully configurable experience__

Many users prefer less visual noise when coding, or only want the Syntax Highlighting feature. We should support those folks by providing an easy way to opt-out of undesired editor features.

3. __Minimize performance overhead__

Some features have been omitted because they had undesirable performance implications. When a company scales their Elm project, this plugin should scale with them!

The goal of this plugin is to ensure every feature works reliably and scales with large Elm codebases. The tradeoff is that it doesn't have as many features as more robust plugins like [the Elm Language Server plugin](https://marketplace.visualstudio.com/items?itemName=Elmtooling.elm-ls-vscode).

The features below have been left out for now, but you might be able to help add them to the official Elm Land plugin:

### __"Find usages" and "Bulk rename"__

Something that would make this plugin even better would be a "Find usages" feature. This would enable folks to quickly scan their Elm codebase for all occurences of any type or value.

Back in 2019, Evan outlined a cool project for the community, [called `elm-find`](https://github.com/elm/projects#elm-find). Just like `elm-format` or `elm` itself, it would have the following characteristics:

1. No constant RAM overhead
2. Fast as possible

If you are interested in creating a super fast tool to help improve the Elm ecosystem, we would love to benefit from your implementation of `elm-find`!

Once that project is available, I can update this plugin to have features like __Find usages__ or __Bulk rename__.

__Hoping to add another feature?__

Come join the [Elm Land Discord](https://join.elm.land) and start a conversation in `#vscode`!
Binary file removed elm-land-0.1.4.vsix
Binary file not shown.
Binary file added elm-land-0.1.5.vsix
Binary file not shown.
11 changes: 6 additions & 5 deletions src/features/elm-format-on-save.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as vscode from "vscode"
import * as os from "os"
import * as child_process from "child_process"
import { Feature } from "./shared/logic"

export const feature: Feature = ({ context }) => {
context.subscriptions.push(
vscode.commands.registerCommand('elmLand.installElmFormat', () => {
const terminal = vscode.window.createTerminal(`Install elm-format`)
terminal.sendText("npm install -g elm-format")
terminal.sendText(`(cd ${os.homedir()} && npm install -g elm-format@0.8.5)`)
terminal.show()
})
)
Expand All @@ -26,7 +27,7 @@ const provideDocumentFormattingEdits = async (
// User should disable this feature in the `[elm]` language settings
try {
let text = await runElmFormat(document)
console.info('formatOnSave', `${Date.now()-start}ms`)
console.info('formatOnSave', `${Date.now() - start}ms`)
return [vscode.TextEdit.replace(getFullDocRange(document), text)]
} catch (_) {
return []
Expand All @@ -41,9 +42,9 @@ function runElmFormat(document: vscode.TextDocument): Promise<string> {
if (err) {
const ELM_FORMAT_BINARY_NOT_FOUND = 127
if (err.code === ELM_FORMAT_BINARY_NOT_FOUND || err.message.includes(`'elm-format' is not recognized`)) {
let response = await vscode.window.showInformationMessage(
'Format on save requires "elm-format"',
{ modal: true, detail: 'Please click "Install" or disable "Format on save" in your settings.' },
let response = await vscode.window.showWarningMessage(
'The "Format on save" feature requires "elm-format"',
{ modal: false },
'Install'
)
if (response === 'Install') {
Expand Down
18 changes: 9 additions & 9 deletions src/features/error-highlighting.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as child_process from 'child_process'
import * as path from 'path'
import * as os from 'os'
import * as vscode from 'vscode'
import * as autodetectElmJson from './shared/autodetect-elm-json'
import { GlobalState } from './shared/autodetect-elm-json'
Expand All @@ -13,16 +14,16 @@ export const feature: Feature = ({ globalState, context }) => {
context.subscriptions.push(
vscode.commands.registerCommand('elmLand.installElm', () => {
const terminal = vscode.window.createTerminal(`Install elm`)
terminal.sendText("npm install -g elm")
terminal.sendText(`(cd ${os.homedir()} && npm install -g elm@0.19.1)`)
terminal.show()
})
)

context.subscriptions.push(
vscode.workspace.onDidOpenTextDocument(document => run(globalState, diagnostics, document, 'open'))
vscode.workspace.onDidOpenTextDocument(document => run(globalState, diagnostics, document))
)
context.subscriptions.push(
vscode.workspace.onDidSaveTextDocument(document => run(globalState, diagnostics, document, 'save'))
vscode.workspace.onDidSaveTextDocument(document => run(globalState, diagnostics, document))
)
context.subscriptions.push(diagnostics)

Expand All @@ -33,7 +34,7 @@ export const feature: Feature = ({ globalState, context }) => {

if (document.uri.fsPath.endsWith('elm.json')) {
await autodetectElmJson.run(globalState)
await run(globalState, diagnostics, document, 'open')
await run(globalState, diagnostics, document)
}
}
context.subscriptions.push(
Expand All @@ -48,8 +49,7 @@ export const feature: Feature = ({ globalState, context }) => {
const run = async (
globalState: GlobalState,
collection: vscode.DiagnosticCollection,
document: vscode.TextDocument,
event: 'open' | 'save'
document: vscode.TextDocument
) => {
let start = Date.now()
// Allow user to disable this feature
Expand Down Expand Up @@ -91,7 +91,7 @@ const run = async (

if (elmFilesToCompile.length > 0) {
await compileElmFile(elmJsonFile, elmFilesToCompile)
console.info('errorHighlighting', `${Date.now()-start}ms`)
console.info('errorHighlighting', `${Date.now() - start}ms`)
}
} else {
console.error(`Couldn't find an elm.json file for ${uri.fsPath}`)
Expand Down Expand Up @@ -127,8 +127,8 @@ const Elm = {
const ELM_BINARY_NOT_FOUND = 127
if (err.code === ELM_BINARY_NOT_FOUND || err.message.includes(`'elm' is not recognized`)) {
let response = await vscode.window.showInformationMessage(
'Error highlighting requires "elm"',
{ modal: true, detail: 'Click "Install" or disable "Error highlighting" in your settings.' },
'The "Error highlighting" feature requires "elm"',
{ modal: false },
'Install'
)

Expand Down

0 comments on commit fcaa85d

Please sign in to comment.