SET-OPTIMIZER sets the implementation of COMPILE, ( xt -- ) for
the current word. A correct implementation of COMPILE, does not
change the semantics in any way, only the implementation of the
semantics.
On 2022-10-01 07:06, Anton Ertl wrote:
SET-OPTIMIZER sets the implementation of COMPILE, ( xt -- ) for
the current word. A correct implementation of COMPILE, does not
change the semantics in any way, only the implementation of the
semantics.
It seems, "set-optimizer" as a basis for such an API is suboptimal,
since you have to describe the same semantics *twice*, and you have a
chance to do it incorrectly.
Actually, if we have a definition that compiles some behavior, the
definition that performs this behavior can be created automatically.
I mean, if we have a definition:
: compile-foo ( -- ) ... ;
that appends behavior "foo" to the current definition,
then a word "foo" can be defined as
: foo [ compile-foo ] ;
Then, why do we need to define both "foo" and "compile-foo" by hands?
Having one of them, another can be created automatically.
A better API for per-word optimization should require the user to define
only the compiler for a word, and the word itself will be created >automatically.
For example:
[: postpone over postpone over ;] "2dup" define-by-compiler
compiler: 2dup ]] over over [[ ;
: value
create ,
[: ( addr -- ) lit, postpone @ ;] does-by-compiler
;
BTW, I don't see why xt should be passed to a compiler (as it's done in >"set-compiler"). In what cases it's useful?
On 2022-10-01 07:06, Anton Ertl wrote:
SET-OPTIMIZER sets the implementation of COMPILE, ( xt -- ) for
the current word. A correct implementation of COMPILE, does not
change the semantics in any way, only the implementation of the
semantics.
It seems, "set-optimizer" as a basis for such an API is suboptimal,
since you have to describe the same semantics *twice*, and you have a
chance to do it incorrectly.
----
Ruvim
On 2022-10-01 07:06, Anton Ertl wrote:
SET-OPTIMIZER sets the implementation of COMPILE, ( xt -- ) for
the current word. A correct implementation of COMPILE, does not
change the semantics in any way, only the implementation of the
semantics.
It seems, "set-optimizer" as a basis for such an API is suboptimal,
since you have to describe the same semantics *twice*, and you have a
chance to do it incorrectly.
Ruvim schrieb am Sonntag, 20. November 2022 um 16:38:51 UTC+1:
It seems, "set-optimizer" as a basis for such an API is suboptimal,
since you have to describe the same semantics *twice*, and you have a
chance to do it incorrectly.
I agree, too much handcrafting required for my taste. What are compilers
good for? _Automatic_ translation from a source to a target language.
Unfortunately or luckily (depending on one's POV) the standard Forth
compiler is ultra-dumb (it can even generate correct code from state-
smart definitions). AFAIU the SET-OPTIMIZER scheme is one way to
enhance or even bypass the dumb compiler for better results through
lots of cryptic meta-information.
I would expect a better Forth compiler
to parse run-time & compile-time stack diagrams to generate the greater
part of this meta-information automatically.
IIRC gforth uses vmgen to preparate Forth code for gcc and relies on
the many optimization passes built into gcc: >https://gcc.gnu.org/onlinedocs/gccint/Passes.html
What does SET-OPTIMIZER do better than gcc's optimizers?
SET-OPTIMIZER seems to be a glorified peep-hole optimiser.
On 2022-10-01 07:06, Anton Ertl wrote:
SET-OPTIMIZER sets the implementation of COMPILE, ( xt -- ) for
the current word. A correct implementation of COMPILE, does not
change the semantics in any way, only the implementation of the
semantics.
It seems, "set-optimizer" as a basis for such an API is suboptimal,
since you have to describe the same semantics *twice*, and you have a
chance to do it incorrectly.
Actually, if we have a definition that compiles some behavior, the definition that performs this behavior can be created automatically.
I mean, if we have a definition:
: compile-foo ( -- ) ... ;
that appends behavior "foo" to the current definition,
then a word "foo" can be defined as
: foo [ compile-foo ] ;
Then, why do we need to define both "foo" and "compile-foo" by hands?
Having one of them, another can be created automatically.
A better API for per-word optimization should require the user to define only the compiler for a word, and the word itself will be created automatically.
For example:
[: postpone over postpone over ;] "2dup" define-by-compiler
compiler: 2dup ]] over over [[ ;
: value
create ,
[: ( addr -- ) lit, postpone @ ;] does-by-compiler
;
BTW, I don't see why xt should be passed to a compiler (as it's done in "set-compiler"). In what cases it's useful?
--
Ruvim
albert@cherry.(none) (albert) writes:
SET-OPTIMIZER seems to be a glorified peep-hole optimiser.
What makes you think so?
COMPILE, is not even a peephole optimizer; it just compiles a single
word.
- anton
COMPILE, is not even a peephole optimizer; it just compiles a single
word.
You almost got me! I thought it has something to do with optimisation.
Ruvim <ruvim.pinka@gmail.com> writes:
On 2022-10-01 07:06, Anton Ertl wrote:
SET-OPTIMIZER sets the implementation of COMPILE, ( xt -- ) for
the current word. A correct implementation of COMPILE, does not
change the semantics in any way, only the implementation of the
semantics.
It seems, "set-optimizer" as a basis for such an API is suboptimal,
since you have to describe the same semantics *twice*, and you have a
chance to do it incorrectly.
Yes.
Actually, if we have a definition that compiles some behavior, the
definition that performs this behavior can be created automatically.
Yes, but ... [see below].
I mean, if we have a definition:
: compile-foo ( -- ) ... ;
that appends behavior "foo" to the current definition,
then a word "foo" can be defined as
: foo [ compile-foo ] ;
Actually it's
: compile-foo ( xt -- ) ... ;
: foo recursive [ ' foo compile-foo ] ;
If COMPILE-FOO just drops the xt, you can just pass 0 to COMPILE-FOO.
Then, why do we need to define both "foo" and "compile-foo" by hands?
Having one of them, another can be created automatically.
The usual usage of SET-COMPILER is in defining words, e.g.
: constant1 ( n "name" -- )
create ,
['] @ set-does>
[: >body @ ]] literal [[ ;] set-optimizer ;
Here you have the advantage that the constant needs only one cell in
addition to the header. Yes, you have the disadvantage that the
SET-DOES> and SET-OPTIMIZER actions might disagree, leading to
incorrect behaviour. An additional aspect here is that this
definition assumes that the value of the constant is not changed.
Could we avoid the redundancy and the potential disagreement? You
suggest creating a colon definition for "name". How could this work?
We have to store N somewhere. What I can come up with is:
: lit, postpone literal ;
: constant2 ( n "name" -- )
>r :noname r> ]] drop literal lit, ; [[ >r
: 0 r@ execute postpone ; r> set-optimizer ;
The definition
5 constant1 five1
takes 6 cells (on a 64-bit machine) in the dictionary, while
5 constant2 five2
takes 16 cells in the dictionary plus 146 Bytes of native code with
the debugging engine on AMD64.
Moreover, I had several bugs in CONSTANT2 until I got it right, but
that could get better with more practice. But will it get better than
the alternative? The code is larger, so that's far from clear.
In any case, it seems to me that the size advantage alone makes the
CONSTANT1 approach preferable. Yes, you describe the same thing
twice, and you may get it wrong in one description while getting it
right in the other, so you have test both implementations separately
(e.g., interpret the word once, and include it in a colon definition,
and use the same tests on it; maybe we could automate that), but such
bugs are rare.
A better API for per-word optimization should require the user to define
only the compiler for a word, and the word itself will be created
automatically.
For example:
[: postpone over postpone over ;] "2dup" define-by-compiler
compiler: 2dup ]] over over [[ ;
: value
create ,
[: ( addr -- ) lit, postpone @ ;] does-by-compiler
;
The first two are alternatives, the third one addresses a different
need. For the VALUE example, how does the implementation work; I can
imagine how it works for the 2DUP examples.
BTW, I don't see why xt should be passed to a compiler (as it's done in
"set-compiler"). In what cases it's useful?
It's useful for getting the value of the constant in CONSTANT1.
It's also the interface of COMPILE,. SET-OPTIMIZER only defines
what COMPILE, does for the word that SET-OPTIMIZER is applied to. If
COMPILE, instead DROPped the xt and only then called the word that we
pass with SET-OPTIMIZER, that works nicely for the 2DUP example, but
how would DOES-BY-COMPILER produce the ADDR that is passed to the xt?
On 2022-11-20 16:43, Anton Ertl wrote:
The usual usage of SET-COMPILER is in defining words, e.g.
: constant1 ( n "name" -- )
create ,
['] @ set-does>
[: >body @ ]] literal [[ ;] set-optimizer ;
To me, "lit," looks far more comprehensible than "]] literal [["
Here you have the advantage that the constant needs only one cell in
addition to the header. Yes, you have the disadvantage that the
SET-DOES> and SET-OPTIMIZER actions might disagree, leading to
incorrect behaviour. An additional aspect here is that this
definition assumes that the value of the constant is not changed.
Could we avoid the redundancy and the potential disagreement? You
suggest creating a colon definition for "name". How could this work?
We have to store N somewhere. What I can come up with is:
: lit, postpone literal ;
: constant2 ( n "name" -- )
>r :noname r> ]] drop literal lit, ; [[ >r
: 0 r@ execute postpone ; r> set-optimizer ;
The definition
5 constant1 five1
takes 6 cells (on a 64-bit machine) in the dictionary, while
5 constant2 five2
takes 16 cells in the dictionary plus 146 Bytes of native code with
the debugging engine on AMD64.
The code is lager since create-does in Gforth avoids duplication of some
code parts (i.e., it utilizes one instance for many definitions). And
since anonymous definition are too heavy in Gforth. For example,
":noname ;" takes 24 bytes (3 cells) in Gforth, 3 bytes (3/4 cells) in >SwiftForth 3.11.6, and 1 byte (1/4 cells) in SP-Forth/4.
Having proper tools, it should not be more difficult.
The compiler in "constant1":
[: >body @ ]] literal [[ ;] ( xt )
The compiler in "constant2":
>r :noname r> ]] drop literal lit, ; [[ ( xt )
They can be expressed far simpler as following.
In "constant1":
[: >body @ lit, ;]
In "constant2":
['] lit, partial1
BTW, I don't see why xt should be passed to a compiler (as it's done in
"set-compiler"). In what cases it's useful?
It's useful for getting the value of the constant in CONSTANT1.
As I can see, what is actually needed in this case is not an xt but a
data field address.
Do we have an example when an xt itself is needed?
Ruvim <ruvim.pinka@gmail.com> writes:
On 2022-10-01 07:06, Anton Ertl wrote:
SET-OPTIMIZER sets the implementation of COMPILE, ( xt -- ) for
the current word. A correct implementation of COMPILE, does not
change the semantics in any way, only the implementation of the
semantics.
It seems, "set-optimizer" as a basis for such an API is suboptimal,
since you have to describe the same semantics *twice*, and you have a
chance to do it incorrectly.
Yes.
Actually, if we have a definition that compiles some behavior, the
definition that performs this behavior can be created automatically.
Yes, but ... [see below].
I mean, if we have a definition:
: compile-foo ( -- ) ... ;
that appends behavior "foo" to the current definition,
then a word "foo" can be defined as
: foo [ compile-foo ] ;
Actually it's
: compile-foo ( xt -- ) ... ;
: foo recursive [ ' foo compile-foo ] ;
If COMPILE-FOO just drops the xt, you can just pass 0 to COMPILE-FOO.
Then, why do we need to define both "foo" and "compile-foo" by hands?
Having one of them, another can be created automatically.
The usual usage of SET-COMPILER is in defining words, e.g.
: constant1 ( n "name" -- )
create ,
['] @ set-does>
[: >body @ ]] literal [[ ;] set-optimizer ;
To me, "lit," looks far more comprehensible than "]] literal [["
Here you have the advantage that the constant needs only one cell in
addition to the header. Yes, you have the disadvantage that the
SET-DOES> and SET-OPTIMIZER actions might disagree, leading to
incorrect behaviour. An additional aspect here is that this
definition assumes that the value of the constant is not changed.
Could we avoid the redundancy and the potential disagreement? You
suggest creating a colon definition for "name". How could this work?
We have to store N somewhere. What I can come up with is:
: lit, postpone literal ;
: constant2 ( n "name" -- )
>r :noname r> ]] drop literal lit, ; [[ >r
: 0 r@ execute postpone ; r> set-optimizer ;
The definition
5 constant1 five1
takes 6 cells (on a 64-bit machine) in the dictionary, while
5 constant2 five2
takes 16 cells in the dictionary plus 146 Bytes of native code with
the debugging engine on AMD64.
The code is lager since create-does in Gforth avoids duplication of some
code parts (i.e., it utilizes one instance for many definitions). And
since anonymous definition are too heavy in Gforth. For example,
":noname ;" takes 24 bytes (3 cells) in Gforth, 3 bytes (3/4 cells) in >SwiftForth 3.11.6, and 1 byte (1/4 cells) in SP-Forth/4.
For colon definitions this difference should not be so drastic.
OTOH, if you provide an optimizer that generates longer code instead of
a definition call, it's probably not a problem that the definition
itself takes more space.
----
Ruvim
Ruvim <ruvim.pinka@gmail.com> writes:...
On 2022-11-20 16:43, Anton Ertl wrote:
The usual usage of SET-COMPILER is in defining words, e.g.
: constant1 ( n "name" -- )
create ,
['] @ set-does>
[: >body @ ]] literal [[ ;] set-optimizer ;
: lit, postpone literal ;
: constant2 ( n "name" -- )
[n:d nip lit, ;] >r
: 0 r@ execute postpone ; r> set-optimizer ;
The whole part after the closure should be the same for every such
defining word (but use the proper xt instead of 0), so yes, this could
be much smaller in source code, something like:
: lit, postpone literal ;
: constant2 ( n "name" -- )
[n:d nip lit, ;] define-by-optimizer ;
Concerning the executable code, the need for a colon definition for
every defined word is still a disadvantage of this approach. For
gforth (the debugging engine) on AMD64 I see 11 cells and 47 Bytes of
native code for five2.
albert@cherry.(none) (albert) writes:
COMPILE, is not even a peephole optimizer; it just compiles a single >>>word.
You almost got me! I thought it has something to do with optimisation.
What makes you think it does not?
SET-OPTIMIZER must not be used for changing the behaviour of
COMPILE,ing the xt (the meaning of COMPILE, is fixed), so the only
correct use is to change the implementation; the primary use is for
improving the generated code (i.e., optimization). A secondary
potential use is instrumentation, but we have not used it for that
yet.
Let's see what happens is we use the most general COMPILE,
implementation instead of the ones installed with SET-OPTIMIZER:
sieve bubble matrix fib fft numbers on a 4GHz Skylake
0.078 0.109 0.044 0.068 0.025 gforth-fast with SET-OPTIMIZER (default) 0.181 0.219 0.138 0.274 0.091 gforth-fast without SET-OPTIMIZER
0.144 0.213 0.100 0.201 0.069 gforth-itc with SET-OPTIMIZER
0.152 0.237 0.102 0.228 0.071 gforth-itc without SET-OPTIMIZER (default)
- anton--
In article <2022Nov23.130243@mips.complang.tuwien.ac.at>,
Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
Let's see what happens is we use the most general COMPILE,
implementation instead of the ones installed with SET-OPTIMIZER:
sieve bubble matrix fib fft numbers on a 4GHz Skylake
0.078 0.109 0.044 0.068 0.025 gforth-fast with SET-OPTIMIZER (default)
0.181 0.219 0.138 0.274 0.091 gforth-fast without SET-OPTIMIZER
0.144 0.213 0.100 0.201 0.069 gforth-itc with SET-OPTIMIZER
0.152 0.237 0.102 0.228 0.071 gforth-itc without SET-OPTIMIZER (default)
My Ubuntu installs gforth 0.7.3.
It helps if you mention the results with that version for comparison,
to give an impression of the progress you have made with optimisation.
(And we can see the benefit if the gforth team pushes a newer
version to Debian.)
Anyway, Debian has been maiming Gforth not just by not delivering the >documentation, but also by making --no-dynamic the default, which
disables a number of Gforth optimizations below the COMPILE, level.
To show what you can expect from Debian/Ubuntu, I also present the
numbers for gforth-fast --no-dynamic.
So you can see the difference between the first and second line as
indication of what improvement you can expect from the Debian
installation of Gforth-1.0, and the difference between the second and
third line of what you can expect from making your own installation of >Gforth-1.0 (plus, you get the documentation). Note that the
improvements from dynamic code generation tend to be larger for bigger >programs; for smaller programs the indirect branch predictors of
modern CPUs work very well, for larger programs a little worse.
- anton--
Ruvim <ruvim.pinka@gmail.com> writes:[...]
BTW, I don't see why xt should be passed to a compiler (as it's done in >>>> "set-compiler"). In what cases it's useful?
It's useful for getting the value of the constant in CONSTANT1.
As I can see, what is actually needed in this case is not an xt but a
data field address.
Do we have an example when an xt itself is needed?
: general-compile, ( xt -- )
postpone literal postpone execute ;
This is the default for the COMPILE, method. It is used whenever no
more specific COMPILE, implementation is installed with SET-OPTIMIZER.
But do we have an example when an xt itself is useful for the compiler
that is set via "set-optimizer"?
lits thenpeephole-compile, ;
Yeah. The free software lawyers at Debian have decided that the info
docs as supplied for Gforth (and many more free programs) do not comply
with their "freedom" standards.
Am Tue, 29 Nov 2022 11:32:46 +0100 schrieb none) (albert:
Yeah. The free software lawyers at Debian have decided that the info
docs as supplied for Gforth (and many more free programs) do not comply
with their "freedom" standards.
Actually, they didn't. They passed https://www.debian.org/vote/2006/
vote_001 “GFDL-licensed works without unmodifiable sections are free”. >Gforth's documentation has no invariant section, so it is free.
It's just plain and simple idiocy. Nothing we can fix (we will mention
that the documentation has no invariant section in the next release notes, >though). Well, we do absolutely everything to make a Debian maintainers
life as easy as possible with the current development system. We even >maintain our own Debian distribution.
--
Bernd Paysan
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 504 |
Nodes: | 16 (2 / 14) |
Uptime: | 19:54:33 |
Calls: | 9,904 |
Files: | 13,797 |
Messages: | 6,345,223 |