Skip to content

Attribute to mark immediate types#188

Closed
willcrichton wants to merge 30 commits into
ocaml:trunkfrom
willcrichton:trunk
Closed

Attribute to mark immediate types#188
willcrichton wants to merge 30 commits into
ocaml:trunkfrom
willcrichton:trunk

Conversation

@willcrichton

Copy link
Copy Markdown
Contributor

This pull request adds support for a new attribute [@@immediate] which marks a type as a immediate, or a non-pointer.

Types marked as immediates won't activate the GC's write barrier upon mutation (as they are not pointers and cannot induce a new pointer from the major to minor heap), which can significantly boost performance in programs relying heavily on mutable state. This feature already exists in the compiler, but it currently does not extend to abstract types. For example:

module Foo : sig type t val x : t ref end = struct
  type t = int
  let x = ref 0
end

In the above module, modifications to Foo.x will invoke the write barrier because the compiler doesn't know that t is necessarily immediate, even though it is implemented as such. However, if you update the module to reflect that:

module Foo : sig type t [@@immediate] val x : t ref end = struct
  type t = int
  let x = ref 0
end

Then subsequent mutations to Foo.x will not call the write barrier. For example, running such mutations in a tight loop:

for i = 0 to 100_000_000 do
  Foo.x := !Foo.x
done

On my machine, the version without the attribute takes 0.51s and the version with the attribute takes 0.14s.

The feature was designed similarly to how variance is implemented in the typechecker, albeit substantially simpler. One can add a check on a type alias or definition:

type t = string [@@immediate]

Which will produce the error message:

File "testsuite/tests/typing-immediate/immediate.ml", line 57, characters 2-31:
Error: Types marked with the immediate attribute must be
   non-pointer types like int or bool

And one can assert the immediacy of an abstract type, which will be checked during ascription:

module Foo : sig type t [@@immediate] end = struct
  type t = string
end

Will cause the error:

File "testsuite/tests/typing-immediate/immediate.ml", line 67, characters 42-70:
Error: Signature mismatch:
   Modules do not match:
     sig type t = string end
   is not included in
     sig type t [@@immediate] end
   Type declarations do not match:
     type t = string
   is not included in
     type t [@@immediate]
   File "testsuite/tests/typing-immediate/immediate.ml", line 67, characters 15-35:
     Expected declaration
   File "testsuite/tests/typing-immediate/immediate.ml", line 68, characters 2-17:
     Actual declaration
   the first is not an immediate type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have no tests where the module signature is separate from the module implementation, for example:

module type S = sig
  type t [@@immediate]
end

module M_valid : S = struct type t = int end
module M_invalid : S = struct type t = string end

module F (M : S) : S = M

module FM_valid = F (struct type t = int end)
module FM_invalid = F (struct type t = string end)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the file with the suggested tests. Thanks!

@andrejbauer

Copy link
Copy Markdown

The first example is broken. Is it supposed to have t = int ref instead of t = int?

@andrejbauer

Copy link
Copy Markdown

I would just like to notice that this looks a lot like a small fragment of an effect system for types.

@willcrichton

Copy link
Copy Markdown
Contributor Author

@andrejbauer apologies, that was a typo. You also couldn't have t = int ref since that would make t a pointer (and hence not an immediate). And interesting observation on the effect system!

@andrejbauer

Copy link
Copy Markdown

If it was a typo, what is the correct example then? I don't get it, I think. Are you just propagating the information that a particular abstract type is "immediate" by hand?

@gasche

gasche commented Jun 1, 2015

Copy link
Copy Markdown
Member

My understanding is that "is immediate" is an intensional property of type that was, so far, not expressible in type declarations/interfaces. This [@@immediate] marker is a way to express immediateness in type signatures, and thus to propagate this information through abstraction/functorization barriers.

I think the only property of types (besides their definition, as the upper bound of the information lattice) that is expressible in signatures today is variance. We lack the way to express injectivity, contractivity, the fact of being an upper- or lower-bound of the subtyping lattice, immediateness, floatiness (those two are only motivated by performance rather than expressivity). Dependent type theories typically also try to propagate (strict) productivity or size information.

I'm not sure we should consider this "propagating information by hand": it needs to be marked explicitly in the signature of abstract types (because only the programmer can decide on the intent of exposing or not those informations, making a compromise between freedom to change implementation vs. information provided by the interface), but then it is propagated by the type-checker in the normal way -- until the next abstraction boundary.

@willcrichton

Copy link
Copy Markdown
Contributor Author

@andrejbauer I updated the example in the PR comment above. And @gasche explains the reasoning quite well!

@lpw25

lpw25 commented Jul 7, 2015

Copy link
Copy Markdown
Contributor

As you said earlier, this doesn't work with functors and prevents using multiple implementations.

Does it matter?

I don't have a specific example to hand, but supporting multiple implementations which change from:

type t = Foo | Bar

to:

type t = Foo | Bar | Baz

seems pretty useful.

@c-cube

c-cube commented Jul 7, 2015

Copy link
Copy Markdown
Contributor

Is it theoretically possible to annotate other types with such attributes? Polymorphic arrays of immediate values (or non-float values) would be nice to have, for instance.

@lpw25

lpw25 commented Jul 7, 2015

Copy link
Copy Markdown
Contributor

Is it theoretically possible to annotate other types with such attributes? Polymorphic arrays of immediate values (or non-float values) would be nice to have, for instance.

This would require a kind system, which is something OCaml deliberately avoids.

@gasche

gasche commented Nov 18, 2015

Copy link
Copy Markdown
Member

We discussed it at the developer meeting today. I supported the design and general approach, and there seemed to be consensus that we could merge it in trunk if there was solid evidence that this improves program performance.

@xavierleroy thus asked for a convincing example of use of this feature in a representative OCaml program, with information on the performance effect of the change. @willcrichton or anyone else, would you be able to provide such a representative example (not a micro bench-mark)? The feature freeze for the next release should be in about a month, so you would need to hurry if you want it in the next release.

@yminsky

yminsky commented Nov 19, 2015

Copy link
Copy Markdown

@xavierleroy Xavier, do you need to see the actual source, or are performance numbers enough? We can probably generate performance results pretty easily, but much of it is for source that we can't easily release.

@xavierleroy

Copy link
Copy Markdown
Contributor

Just some performance results, that would be perfect. Thanks!

@damiendoligez

Copy link
Copy Markdown
Member

@yminsky do you have numbers now?

@damiendoligez damiendoligez added this to the maybe-4.03.0 milestone Jan 25, 2016
@yminsky

yminsky commented Jan 25, 2016

Copy link
Copy Markdown

Apologies. We let this slip, but @lpw25 will throw together a benchmark very shortly.

@lpw25

lpw25 commented Jan 26, 2016

Copy link
Copy Markdown
Contributor

We have an in memory database thing that uses integers for keys and some other stuff. This is used in production (i.e. not a toy example). Benchmarks for its main operations, without = private int:

┌─────────────────────────┬──────────┬─────────┬────────────┬───────────┬────────────┐
│ Name                    │ Time/Run │ mWd/Run │   mjWd/Run │  Prom/Run │ Percentage │
├─────────────────────────┼──────────┼─────────┼────────────┼───────────┼────────────┤
│ A                       │  22.58ms │  2.21kw │ 1_804.52kw │   942.19w │     46.66% │
│ B                       │  31.29ms │  2.21kw │ 1_804.52kw │   942.27w │     64.66% │
│ C                       │  25.39ms │  2.93kw │ 1_804.96kw │ 1_383.22w │     52.45% │
│ D                       │  38.79ms │  2.93kw │ 1_804.96kw │ 1_383.31w │     80.15% │
│ E                       │  33.26ms │  3.61kw │ 1_805.52kw │ 1_938.27w │     68.73% │
│ F                       │  48.40ms │  3.61kw │ 1_805.52kw │ 1_938.41w │    100.00% │
│ G                       │  12.68ms │  2.98kw │   131.09kw │    21.10w │     26.20% │
└─────────────────────────┴──────────┴─────────┴────────────┴───────────┴────────────┘

and with = private int:

┌─────────────────────────┬──────────┬─────────┬────────────┬───────────┬────────────┐
│ Name                    │ Time/Run │ mWd/Run │   mjWd/Run │  Prom/Run │ Percentage │
├─────────────────────────┼──────────┼─────────┼────────────┼───────────┼────────────┤
│ A                       │  21.11ms │  2.21kw │ 1_804.52kw │   942.18w │     51.92% │
│ B                       │  29.08ms │  2.21kw │ 1_804.52kw │   942.25w │     71.51% │
│ C                       │  23.48ms │  2.93kw │ 1_804.96kw │ 1_383.20w │     57.73% │
│ D                       │  32.88ms │  2.93kw │ 1_804.96kw │ 1_383.27w │     80.86% │
│ E                       │  26.37ms │  3.61kw │ 1_805.52kw │ 1_938.22w │     64.84% │
│ F                       │  40.67ms │  3.61kw │ 1_805.52kw │ 1_938.34w │    100.00% │
│ G                       │  10.78ms │  2.98kw │   131.09kw │    21.17w │     26.50% │
└─────────────────────────┴──────────┴─────────┴────────────┴───────────┴────────────┘

which gives an average improvement of just under 15%. [@@immediate] would allow us to keep this improvement without exposing the internal int type in the interfaces.

@mshinwell

Copy link
Copy Markdown
Contributor

@xavierleroy Are you satisfied with these numbers?

@damiendoligez

Copy link
Copy Markdown
Member

Travis is failing, probably because you need to update the .depend files.

I talked with @xavierleroy and we're both OK to merge this.

@mshinwell

Copy link
Copy Markdown
Contributor

@diml is going to do the rebase and then push it directly.

@ghost

ghost commented Feb 1, 2016

Copy link
Copy Markdown

I squashed the commits and rebased them on the current trunk. We'll reread the result tomorrow

@ghost

ghost commented Feb 2, 2016

Copy link
Copy Markdown

Merged manually (50dd38d)

@ghost ghost closed this Feb 2, 2016
mshinwell pushed a commit to mshinwell/ocaml that referenced this pull request Jun 26, 2020
lthls pushed a commit to lthls/ocaml that referenced this pull request Sep 23, 2020
lthls pushed a commit to lthls/ocaml that referenced this pull request Sep 23, 2020
lthls pushed a commit to lthls/ocaml that referenced this pull request Sep 24, 2020
chambart pushed a commit to chambart/ocaml-1 that referenced this pull request Sep 9, 2021
EmileTrotignon pushed a commit to EmileTrotignon/ocaml that referenced this pull request Jan 12, 2024
* reintegrate toplevel and remove hilite fmt dependency

* add basic regexp-based syntax highlighting in toplevel

* resize on tab completion

* hide try button

* scroll output on completion

* highlight double-quoted strings

* fix multiple matches when highlighting

* add changes entry
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.