• Stars
    star
    198
  • Rank 196,898 (Top 4 %)
  • Language PureScript
  • License
    MIT License
  • Created over 2 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Optimizing backend toolkit and modern ECMAScript backend for PureScript

purescript-backend-optimizer

An optimizing backend toolkit for PureScript's CoreFn.

Overview

PureScript's built-in optimizer leaves a lot on the table by only performing naive syntactic rewrites in the JavaScript specific backend. purescript-backend-optimizer consumes the compiler's high-level IR (CoreFn) and applies a more aggressive inlining pipeline (subsuming existing optimizations) that is backend agnostic.

It additionally ships with an alternative code-generator which outputs modern ECMAScript with additional runtime optimizations, resulting in lighter, faster bundles.

Example Input purs purs-backend-es
Lenses Input Output Output
Prisms Input Output Output
Variant Input Output Output
Heterogeneous Input Output Output
Uncurried CPS Input Output Output
Generics Input Output Output
Fusion (Fold) Input Output Output
Fusion (Unfold) Input Output Output
Recursion Schemes Input Output Output
HTML DSL Input Output Output
Imperative Loops Input Output Output

ECMAScript Backend

Install

npm install purs-backend-es

Usage

purs-backend-es requires PureScript 0.15.4 or greater. Add it as a backend in your spago.dhall.

+, backend = "purs-backend-es build"

You should likely only do this for a production build configuration, since optimization and code-generation are currently not incremental. For example, you can create a separate prod.dhall with the following:

./spago.dhall // { backend = "purs-backend-es build" }

By default, purs-backend-es will read corefn.json files from output, and generate code in output-es following the same directory pattern as the compiler's JS backend.

See the CLI help for options:

purs-backend-es --help

spago bundle-app is not compatible with purs-backend-es. To bundle your app, you can invoke purs-backend-es bundle-app. It supports the same CLI arguments as spago bundle-app.

spago build && purs-backend-es bundle-app --no-build

Notable Differences from purs

  • Uses arrow functions, const/let block scope, and object spread syntax.
  • Uses a much lighter-weight data encoding (using plain objects) which is significantly faster to dispatch. By default, we use string tags, but integer tags are also supported via a CLI flag for further performance improvement and size reduction.
  • Newtypes over Effect and ST also benefit from optimizations. With general inlining, even instances that aren't newtype-derived benefit from the same optimizations.
  • TCO fires in more cases. For example, you can now write TCO loops over purescript-exists because the eliminator is inlined away.
  • TCO supports mutually recursive binding groups.
  • Optimized pattern matching eliminates redundant tests.

Code size and performance improvement varies by usecase, but we've generally observed:

  • 25-35% improvement in runtime.
  • 20-25% improvement in minified bundle size.
  • 15-20% improvement in minified+gzip bundle size.

Inlining Directives

The inliner follows some basic heuristics, but to get the most out of it you should configure inlining directives. An inlining directive tells the optimizer under what conditions it should inline a definition.

The following inlining directives are supported:

  • default - A definition is inlined using default heuristics (unspecified).
  • never - A definition is never inlined.
  • always - A definition is inlined at every reference.
  • arity=n - Where n is a positive integer, a definition is inlined when at least n arguments are applied.

An inlining directive may be applied to a top-level binding or top-level accessor.

Syntax

module Example where

import Prelude

myAdd :: Int -> Int -> Int
myAdd a b = a + b

The myAdd function would likely already be inlined since it is so small, but to guarantee that it is always inlined after two argments are applied, you would write the following directive:

Example.myAdd arity=2

For instance methods, you should use named instances and a top-level accessor:

module Example where

import Prelude

data MyData = Zero | One

instance semigroupMyData :: Semigroup MyData where
  append = case _, _ of
    Zero, _ -> Zero
    _, Zero -> Zero
    _, _ -> One
Example.semigroupMyData.append arity=2

It's possible to refer to unnamed instances through their compiler-generated name, however this is quite difficult to maintain.

Sometimes instances are parameterized by other constraints:

module Example where

import Prelude

data Product f g a = Product (f a) (g a)

instance functorProduct :: (Functor f, Functor g) => Functor (Product f g) where
  map f (Product a b) = Product (f <$> a) (f <$> b)
Example.functorProduct(..).map arity=2

Note the (..) between the name and the accessor, which will match applications of known instance dictionaries.

Configuration

Inlining directives can be configured in three ways:

Module-specific inlining directives via a module header

In any given module header you can add @inline comments with the above syntax:

-- @inline Example.myAdd arity=2
module AnotherExample where

import Example
...

Directives configured this way only apply to the current module.

Global inlining directives via a module header

In any given module header, you can add @inline export directives for definitions in the current module:

-- @inline export myAdd arity=2
-- @inline export semigroupMyData.append arity=1
module Example where
...

Directives configured this way apply to the current module and downstream modules.

Note: They must be defined in the module header to due to an upstream compiler limitation.

Global inlining directives via a configuration file

You can provide a directive file to purs-backend-es:

purs-backend-es build --directives my-directives.txt

Each line should contain an inlining directive using the above syntax, with the additional support of -- line comments. These directives will take precedence over any defaults or exported directives, so you can tweak inlining for your project however you see fit.

Cheatsheet

Precedence applies in the following order (most specific to least specific):

Location Affects
Module A's header, @inline module B directive Module B's usages in module A
Directives file All modules
Module A's header, @inline export module A directive Module A's usages in all modules
Default heuristics All modules

Tracing Optimizations

purs-backend-es can also trace the rewrite passes taken when optimizing a top-level expression via the --trace-rewrites CLI arg. This may help in debugging an unexpected or non-optimal result.

Semantics

purescript-backend-optimizer consumes the PureScript compiler's high-level intermediate representation (IR) known as CoreFn. CoreFn has no defined evaluation semantics, but we operate under assumptions based on common use:

  • We make decisions on what to keep or discard using Fast and Loose Reasoning, assuming that CoreFn is pure and total.

    In practical terms, this means we may take the opportunity to remove any code that we know for certain is not demanded. However, at times we may also choose to propagate known bottoms. Thus, non-totality is considered undefined behavior for the purposes of CoreFn's semantics.

  • We preserve sharing of redexes under common assumptions of call-by-value semantics. Like non-totality, we consider a specific evaluation order to be undefined behavior in CoreFn. However, we assume that all terms under a redex should be in normal form.

    In practical terms, this means we will not delay function arguments that most would expect to be evaluated immediately.

More Repositories

1

avd

Arista Validated Designs
Python
270
star
2

goarista

Fairly general building blocks used in Arista Go code and open-sourced for the benefit of all.
Go
197
star
3

nix-serve-ng

A drop-in replacement for nix-serve that is faster and more reliable
Haskell
156
star
4

EosSdk

EOS SDK - write native apps for your Arista switch
C++
147
star
5

netdevops-examples

Examples of using DevOps tools with Arista EOS and CloudVision
Jupyter Notebook
121
star
6

bst

A one-stop shop for process isolation
C
99
star
7

openmgmt

Documentation and examples for using open network management tools such as OpenConfig
Go
68
star
8

ansible-cvp

Ansible modules for Arista CloudVision
Python
66
star
9

j2lint

Jinja2 Linter CLI
Python
52
star
10

anta

What do you call an ant with frogs legs?
Python
46
star
11

cvprac

Python
45
star
12

goeapi

Go library for Arista's eAPI command API implementation
Go
43
star
13

CloudVisionPortal-Examples

A collection of CloudVision Portal examples and best practices
Python
40
star
14

yang

YANG models published and supported by Arista Networks
Makefile
36
star
15

ctypegen

Generate ctypes boilerplate code from debugging information; Use python to mock C code for testing
Python
28
star
16

cloudvision-python

Python resources and libraries for integrating with Arista's CloudVision platform
Python
25
star
17

atd-public

Python
24
star
18

openconfigbeat

Elastic Beat for OpenConfig
Go
22
star
19

sonic

Open source drivers and initialization library for Arista platforms running SONiC
Python
22
star
20

cloudvision

Resources and documentation for Arista's CloudVision platform
TypeScript
21
star
21

puppet-eos

Arista EOS modules for automating network resources using Puppet
20
star
22

avd-workshops

Arista Automation Workshop
CSS
20
star
23

eoscentral

Code examples associated with EOS Central articles.
17
star
24

cloudvision-apis

gRPC APIs for integrating with Arista's CloudVision platform
16
star
25

robotframework-aristalibrary

Robot Framework library for Arista EOS
Python
16
star
26

terraform-provider-cloudeos

Arista CloudEOS Terraform Provider
Go
14
star
27

vane

A weather vane is an instrument used for showing the direction of the wind. Just like a weather vane, Vane is a network certification tool that shows a network's readiness for production based on validation tests.
Python
12
star
28

arista-ceoslab-operator

K8s operator for managing meshnet-networked cEOS-lab instances
Go
11
star
29

CloudEOS

HCL
11
star
30

swi-tools

Scripts for handling Arista SWI and SWIX files
Python
11
star
31

cloudvision-ztpaas-utils

Utilities for ZTP as a Service with CloudVision
Python
11
star
32

terraform-provider-cloudvision

Go
10
star
33

cloudvision-go

Go resources and libraries for integrating with Arista's CloudVision platform
Go
10
star
34

cloudvision-python-actions

Example Python action scripts for integrating with Change Controls in Arista's CloudVision platform
Python
9
star
35

arcomm

A command-line utility and library for communicating with Aristas
Python
9
star
36

ci-workshops-fundamentals

Network automation fundamentals repository to deliver fundamentals workshops.
Jinja
8
star
37

pytest-netdut

A pytest library for testing software on network devices.
Python
7
star
38

arista-onie-installer

ONIE installer for Arista's EOS
6
star
39

pyopenconfig

Python implementation of the gRPC service for interacting with network devices based on OpenConfig models (DEPRECATED)
Python
6
star
40

dom

The Digital Optical Monitor script will periodically poll the optical power levels of each interface on a switch and generate syslog events when the transmit (Tx) or Receive (Rx) power levels change beyond the threshold. Optionally, SNMP v2c traps or v3 informs may be generated, as well.
Python
5
star
41

telegraf-cloudvision

Go
5
star
42

eos-eapi-rust

Rust
5
star
43

go-cvprac

Go
4
star
44

eos_boot_loader

Shell
4
star
45

training-infra-public

Arista training lab infrastructure
Python
4
star
46

ansible-eos

Ansible modules for Arista Network's EOS
Python
4
star
47

quantumfs

A distributed FUSE filesystem optimized for large-scale software development
Go
4
star
48

gomap

Golang hash map with user provided hash and equal functions
Go
4
star
49

telemetry-email-alerter

Python script that allows you to subscribe to Arista Telemetry Events and then send them to an SMTP server for email notifications.
Python
3
star
50

eos-deployment-guide-configs

Configs from EOS Deployment Guides
3
star
51

switch-interface-maps

This is a set of interface maps (json) for as many SKUs (DC and Campus) to be used in conjunction with the Arbitrary Interface Mapping feature
3
star
52

net-snmp

Arista Networks' net-snmp patches
C
3
star
53

pcapinspect

Python
3
star
54

openfdk

Open FPGA Developer's Kit
VHDL
3
star
55

ServiceNowRac

ServiceNow RESTful Client
Python
3
star
56

aql-examples

Sharing AQL (Advanced Query Language for CVP) examples with customers
Python
3
star
57

ownership-voucher-grpc

2
star
58

chef-eos

Chef cookbook for Arista EOS.
Ruby
2
star
59

cloudvision-frontend-config

JavaScript, TypeScript and Eslint configuration for CloudVision frontend libraries.
JavaScript
2
star
60

sonic-firmware

SONiC Firmware
2
star
61

docker-logstash

Container for logstash with input from the kafka feed from Arista's OpenConfig client
2
star
62

aclabs

Python
2
star
63

ci-workshops-avd

AVD workshop repository to deliver workshop content specific to AVD.
Makefile
2
star
64

promtail_extension

Python
1
star
65

eos-external-tools

Go
1
star
66

probe-tools

Some test tools for RFC8335 PROBE aka ICMP Extended Echo
Python
1
star
67

tech-library-comments

1
star
68

eossdkrpc

EOS SDK RPC protobuf files and information
1
star
69

quicktrace-public

The open source version of QuickTrace
C++
1
star
70

python-aristaproto

Python
1
star