Hi everyone,
Unfortunately, I think I have stumbled upon a major problem for my project with the linking model of Gambit / Scheme's lack of specification for linking code.
For my applications I absolutly need the ability to be able to load code selectively. If I understand correctly, the Gambit linking model is that all classes compiled into a .exe are loaded upon launching the .exe so that cannot do for me.
The only alternative I see is the use of .o1 loadable code but is it reasonable to dynamically load like 1000+ .o1 files? Wouldn't launching incur a huge penalty in speed / space vs a selfcontained .exe as every .o1 is a full fleged dynamic library? Also, with this approach, distributing to my clients a folder containing 1000 .o1 files is everything but nice. I could zip them together a bit like interpreted languages like Python do but then I don't think Windows or Gambit or ... can load .o1 files from a zip directly so I'd have to unzip them to a temp folder which is again not very tempting.
Any help / thoughts really appreciated. This is criticaly for my using Gambit for my project.
Thanks,
Guillaume
Afficher les réponses par date
FWIW, my thoughts:
You want to be able to load code selectively (on demand, not on startup, if I understand correctly), but then you don't want to do it because of loading speed concerns--that's sort of contradicting itself, isn't it? :)
With my chjmodule stuff, I'm going the .o1 file route; the programs I've been writing up to now are loading maybe 20 or 30 such object files only, not thousands. (It actually *is* a slow process with chjmodule, but not because of the loading of the object files themselves but because of the aliasing (copying) of the identifyers to the other namespaces; I'm using eval (like (eval `(define ,id1 ,id2))) for this and that's slow (I'm sure that can be improved, I just haven't bothered).)
If loading speed or the number of modules is an issue, maybe you could group together modules which are usually loaded together (by using, say, |include|), maybe reducing the 1000 items to a few hundred or less? (Gambit can also do a better job optimizing when you group together code (block compilation with inlining).) Dunno about zipping, one of the good things of shared object files is that they are mmap'ed into the process so they are shared by multiple independent processes; this is lost if each process extracts the objects to private memory.
What's the reason why you can't live with the code being already loaded at launch time?
Christian.
Let me try to elaborate.
For some background, I am in the process of porting the C++ Kernel of JazzScheme (a Scheme like programming language) to Gambit.
I'll talk in terms of classes as in Jazz the association to source code is done by class names but it is not essential in nature.
One essential feature of JazzScheme is the following: - Lets say I build an application with all its classes compiled into one executable. This is very important as some classes are so lowlevel than using many of them interpreted can really slow down the application - While running this application, I decide to make a live change to the code of lets say class F by evaluating some part of the source code file lets say F.jazz (this is possible as the function that was compiled is replaced by a new interpreted one and compiled and interpreted code can freely mix as in Gambit) - Here's the catch... Next time I launch the application, when loading class F, Jazz will first check the modification time of F.jazz and because it is more recent than the modification time of the internal compiled version, will load the class from the source code making in it unnecessary to always rebuild applications. This is possible because, even in the executable, code has to be explicitly loaded. In other words, the semantics of compiled and interpreted code is the same in regards to 'load'. This is one thing I do not see how to do with Gambit and that cannot really be solved by packaging into separate modules of related files.
PS: Marc: I was thinking how I guess Gambit must do some linking work when loading the compiled C code representing a .scm file to make its symbols available to the runtime. Couldn't this linking job be separated so that by default it is done automatically but it can also be put in a mode where it needs to be explicitly called?
Guillaume
Christian Jaeger wrote:
FWIW, my thoughts:
You want to be able to load code selectively (on demand, not on startup, if I understand correctly), but then you don't want to do it because of loading speed concerns--that's sort of contradicting itself, isn't it? :)
With my chjmodule stuff, I'm going the .o1 file route; the programs I've been writing up to now are loading maybe 20 or 30 such object files only, not thousands. (It actually *is* a slow process with chjmodule, but not because of the loading of the object files themselves but because of the aliasing (copying) of the identifyers to the other namespaces; I'm using eval (like (eval `(define ,id1 ,id2))) for this and that's slow (I'm sure that can be improved, I just haven't bothered).)
If loading speed or the number of modules is an issue, maybe you could group together modules which are usually loaded together (by using, say, |include|), maybe reducing the 1000 items to a few hundred or less? (Gambit can also do a better job optimizing when you group together code (block compilation with inlining).) Dunno about zipping, one of the good things of shared object files is that they are mmap'ed into the process so they are shared by multiple independent processes; this is lost if each process extracts the objects to private memory.
What's the reason why you can't live with the code being already loaded at launch time?
Christian.
Guillaume,
would it be possible to simply load the whole application at once, and then only "patch" the application with all the interpreted classes? If no class has been modified, then you get a fast startup time.
Maybe this is too simplistic. If you have side-effects in "static" portions of your classes (like in Java), this may not work, of course. But you know better than me.
Dominique
Let me try to elaborate.
For some background, I am in the process of porting the C++ Kernel of JazzScheme (a Scheme like programming language) to Gambit.
I'll talk in terms of classes as in Jazz the association to source code is done by class names but it is not essential in nature.
One essential feature of JazzScheme is the following:
- Lets say I build an application with all its classes compiled into
one executable. This is very important as some classes are so lowlevel than using many of them interpreted can really slow down the application
- While running this application, I decide to make a live change to
the code of lets say class F by evaluating some part of the source code file lets say F.jazz (this is possible as the function that was compiled is replaced by a new interpreted one and compiled and interpreted code can freely mix as in Gambit)
- Here's the catch... Next time I launch the application, when loading
class F, Jazz will first check the modification time of F.jazz and because it is more recent than the modification time of the internal compiled version, will load the class from the source code making in it unnecessary to always rebuild applications. This is possible because, even in the executable, code has to be explicitly loaded. In other words, the semantics of compiled and interpreted code is the same in regards to 'load'. This is one thing I do not see how to do with Gambit and that cannot really be solved by packaging into separate modules of related files.
PS: Marc: I was thinking how I guess Gambit must do some linking work when loading the compiled C code representing a .scm file to make its symbols available to the runtime. Couldn't this linking job be separated so that by default it is done automatically but it can also be put in a mode where it needs to be explicitly called?
Guillaume
Christian Jaeger wrote:
FWIW, my thoughts:
You want to be able to load code selectively (on demand, not on startup, if I understand correctly), but then you don't want to do it because of loading speed concerns--that's sort of contradicting itself, isn't it?
:)
With my chjmodule stuff, I'm going the .o1 file route; the programs I've been writing up to now are loading maybe 20 or 30 such object files only, not thousands. (It actually *is* a slow process with chjmodule, but not because of the loading of the object files themselves but because of the aliasing (copying) of the identifyers to the other namespaces; I'm using eval (like (eval `(define ,id1 ,id2))) for this and that's slow (I'm sure that can be improved, I just haven't
bothered).)
If loading speed or the number of modules is an issue, maybe you could group together modules which are usually loaded together (by using, say, |include|), maybe reducing the 1000 items to a few hundred or less? (Gambit can also do a better job optimizing when you group together code (block compilation with inlining).) Dunno about zipping, one of the good things of shared object files is that they are mmap'ed into the process so they are shared by multiple independent processes; this is lost if each process extracts the objects to private memory.
What's the reason why you can't live with the code being already loaded at launch time?
Christian.
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
schemeway@sympatico.ca wrote:
Guillaume,
would it be possible to simply load the whole application at once, and then only "patch" the application with all the interpreted classes? If no class has been modified, then you get a fast startup time.
Maybe this is too simplistic. If you have side-effects in "static" portions of your classes (like in Java), this may not work, of course. But you know better than me.
Dominique
Yes. You're right for the static side effects. And also, suppose a file F with (class F extends Window ...) that became (interface F extends (I J) ...)! Imagine the challenge to undo all the runtime initializations done by class F!
Note that after thinking about the problem, I could probably do my own delaying by doing in essence something like
(define x #f) (define y #f) (define z #f)
(register-class-initializer 'F (lambda () (set! x ...) (set! y ...) (set! z ...)))
One thing that is annoying with that, is that Gambit won't be able to do many optimizations because of my code mutating all my variables.
Guillaume
Let me try to elaborate.
For some background, I am in the process of porting the C++ Kernel of JazzScheme (a Scheme like programming language) to Gambit.
I'll talk in terms of classes as in Jazz the association to source code is done by class names but it is not essential in nature.
One essential feature of JazzScheme is the following:
- Lets say I build an application with all its classes compiled into
one executable. This is very important as some classes are so lowlevel than using many of them interpreted can really slow down the application
- While running this application, I decide to make a live change to
the code of lets say class F by evaluating some part of the source code file lets say F.jazz (this is possible as the function that was compiled is replaced by a new interpreted one and compiled and interpreted code can freely mix as in Gambit)
- Here's the catch... Next time I launch the application, when loading
class F, Jazz will first check the modification time of F.jazz and because it is more recent than the modification time of the internal compiled version, will load the class from the source code making in it unnecessary to always rebuild applications. This is possible because, even in the executable, code has to be explicitly loaded. In other words, the semantics of compiled and interpreted code is the same in regards to 'load'. This is one thing I do not see how to do with Gambit and that cannot really be solved by packaging into separate modules of related files.
PS: Marc: I was thinking how I guess Gambit must do some linking work when loading the compiled C code representing a .scm file to make its symbols available to the runtime. Couldn't this linking job be separated so that by default it is done automatically but it can also be put in a mode where it needs to be explicitly called?
Guillaume
Christian Jaeger wrote:
FWIW, my thoughts:
You want to be able to load code selectively (on demand, not on startup, if I understand correctly), but then you don't want to do it because of loading speed concerns--that's sort of contradicting itself, isn't it?
:)
With my chjmodule stuff, I'm going the .o1 file route; the programs I've been writing up to now are loading maybe 20 or 30 such object files only, not thousands. (It actually *is* a slow process with chjmodule, but not because of the loading of the object files themselves but because of the aliasing (copying) of the identifyers to the other namespaces; I'm using eval (like (eval `(define ,id1 ,id2))) for this and that's slow (I'm sure that can be improved, I just haven't
bothered).)
If loading speed or the number of modules is an issue, maybe you could group together modules which are usually loaded together (by using, say, |include|), maybe reducing the 1000 items to a few hundred or less? (Gambit can also do a better job optimizing when you group together code (block compilation with inlining).) Dunno about zipping, one of the good things of shared object files is that they are mmap'ed into the process so they are shared by multiple independent processes; this is lost if each process extracts the objects to private memory.
What's the reason why you can't live with the code being already loaded at launch time?
Christian.
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
From the point of view of reloading, there does not seem to be any
difference between statically linked objects and .oX object files! You can link everything into one executable file and still "patch" that binary exactly as if you had loaded all objects as .oX files. No need for set! hackery. You can "have your cake and eat it too".
The rules which identifyers are fixed at compiletime through block mode are still the same, regardless of linking mode. If you use the same "block" granularity in building your .oX 'patch' files as you have used to build your exe, there should be no problem.
$ cat test-load.scm (declare (block) (standard-bindings) (extended-bindings))
(define (print-world) (display (list "Hello " world "\n")))
(print-world) (##repl)
$ cat test-load-one.scm (declare (block) (standard-bindings) (extended-bindings))
(define world "World")
(define (remote-print-world) (display (list "Hello " world "\n")))
$ gsc -link test-load-one test-load test-load-one: test-load:
$ gcc -O3 -fomit-frame-pointer -mpreferred-stack-boundary=2 test-load.c test-load-one.c test-load_.c -I/usr/local/Gambit-C/current/include -L/usr/local/Gambit-C/current/lib -lgambc -lm -lutil -ldl -o test-load
$ ./test-load Hello World
(remote-print-world)
Hello World
change world in test-load-one.scm to "new World", from another shell issue $ gsc test-load-one
then in the old running instance:
(load "test-load-one")
"/home/chris/schemedevelopment/gambit/work/test-load-one.o1"
(remote-print-world)
Hello new World ;; ^- this is because remote-print-world has been recompiled and ;; reloaded as well.
(print-world)
Hello new World
$ l -rw-rw-r-- 1 chris chris 304 2007-06-14 10:45 test-load.scm -rw-rw-r-- 1 chris chris 4679 2007-06-14 10:51 test-load.c -rw-rw-r-- 1 chris chris 389741 2007-06-14 10:51 test-load_.c -rwxrwxr-x 1 chris chris 17323 2007-06-14 10:51 test-load -rw-rw-r-- 1 chris chris 179 2007-06-14 10:51 test-load-one.scm -rwxrwxr-x 1 chris chris 7906 2007-06-14 10:52 test-load-one.o1
More info on that.
Jazz has 100s of classes. Some classes are compiled in Launcher.exe, but when the loader detects that the interpreted file is newer, it will load through the interpreter and not through the C call in the *.exe.
At 17:36 -0400 2007/06/13, Guillaume Cartier wrote:
Hi everyone,
Unfortunately, I think I have stumbled upon a major problem for my project with the linking model of Gambit / Scheme's lack of specification for linking code.
For my applications I absolutly need the ability to be able to load code selectively. If I understand correctly, the Gambit linking model is that all classes compiled into a .exe are loaded upon launching the .exe so that cannot do for me.
The only alternative I see is the use of .o1 loadable code but is it reasonable to dynamically load like 1000+ .o1 files? Wouldn't launching incur a huge penalty in speed / space vs a selfcontained .exe as every .o1 is a full fleged dynamic library? Also, with this approach, distributing to my clients a folder containing 1000 .o1 files is everything but nice. I could zip them together a bit like interpreted languages like Python do but then I don't think Windows or Gambit or ... can load .o1 files from a zip directly so I'd have to unzip them to a temp folder which is again not very tempting.
Any help / thoughts really appreciated. This is criticaly for my using Gambit for my project.
Thanks,
Guillaume _______________________________________________ Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 13-Jun-07, at 5:36 PM, Guillaume Cartier wrote:
Hi everyone,
Unfortunately, I think I have stumbled upon a major problem for my project with the linking model of Gambit / Scheme's lack of specification for linking code.
For my applications I absolutly need the ability to be able to load code selectively. If I understand correctly, the Gambit linking model is that all classes compiled into a .exe are loaded upon launching the .exe so that cannot do for me.
The only alternative I see is the use of .o1 loadable code but is it reasonable to dynamically load like 1000+ .o1 files?
To evaluate how "reasonable" this is I wrote a script to generate 1000 .scm files. Each contains a dozen Scheme functions which compile down to a 54 KB dynamic library (.o1 file). According to "nm" each file contains about 36 KB of executable code, so that's a total of 36 MB of code (which is probably an order of magnitude larger than a big application).
I then wrote the following file (go.scm):
(load "mod-000000.o1") (load "mod-000001.o1") ... (load "mod-0000999.o1") (pp (fn-000047 0))
And loaded it into the Gambit interpreter running on a MacBook Pro. Here's the timing of a few runs:
% time gsi go.scm 10865
real 0m1.868s user 0m0.353s sys 0m0.450s % time gsi go.scm 10865
real 0m0.895s user 0m0.328s sys 0m0.394s % time gsi go.scm 10865
real 0m0.899s user 0m0.326s sys 0m0.387s
You can see that the second and third runs are faster than the first one, because the files have been loaded into the disk cache. When the number of .o1 files is lowered to 100 files the timing is:
% time gsi go.scm 10865
real 0m0.229s user 0m0.192s sys 0m0.034s
So it takes about one second of real time to load 36 MB of executable code distributed in 1000 .o1 files and about a quarter of a second of real time to load 3.6 MB of executable code distributed in 100 .o1 files.
I think that is reasonable.
Wouldn't launching incur a huge penalty in speed / space vs a selfcontained .exe as every .o1 is a full fleged dynamic library? Also, with this approach, distributing to my clients a folder containing 1000 .o1 files is everything but nice.
Why is that a problem? You just need to package your .o1 files in a .tar.gz and have a package installer that unpacks the archive at the appropriate place. In fact that's essentially how the Snow framework operates.
I could zip them together a bit like interpreted languages like Python do but then I don't think Windows or Gambit or ... can load .o1 files from a zip directly so I'd have to unzip them to a temp folder which is again not very tempting.
Any help / thoughts really appreciated. This is criticaly for my using Gambit for my project.
I don't really see a problem with this (either performance or convenience).
Marc
Thanks Marc. These results are *very* interesting!
2 questions:
1- Will Gambit do as good a job in optimizing the code if I package my application as a bunch of .o1 files than if I had compiled them together in one executable? 2- If the answer to 1- is yes, then the only remaining thorn is to have to actually untar the .o1 files. Almost all modern languages like Python (.pyc), Java (.class), ... enable code distribution inside compressed archives which is really nice and easy for the developer and for the end user (it would annoy me if Mercurial upon install extracted it's 300+ .pyc files to my hard disk instead of keeping them in a neat library.zip file). Do you think something could be done to load the .o1 files directly from an archive? This could be a wonderful code distribution mechanism for Gambit too.
Guillaume
Marc Feeley wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 13-Jun-07, at 5:36 PM, Guillaume Cartier wrote:
Hi everyone,
Unfortunately, I think I have stumbled upon a major problem for my project with the linking model of Gambit / Scheme's lack of specification for linking code.
For my applications I absolutly need the ability to be able to load code selectively. If I understand correctly, the Gambit linking model is that all classes compiled into a .exe are loaded upon launching the .exe so that cannot do for me.
The only alternative I see is the use of .o1 loadable code but is it reasonable to dynamically load like 1000+ .o1 files?
To evaluate how "reasonable" this is I wrote a script to generate 1000 .scm files. Each contains a dozen Scheme functions which compile down to a 54 KB dynamic library (.o1 file). According to "nm" each file contains about 36 KB of executable code, so that's a total of 36 MB of code (which is probably an order of magnitude larger than a big application).
I then wrote the following file (go.scm):
(load "mod-000000.o1") (load "mod-000001.o1") ... (load "mod-0000999.o1") (pp (fn-000047 0))
And loaded it into the Gambit interpreter running on a MacBook Pro. Here's the timing of a few runs:
% time gsi go.scm 10865
real 0m1.868s user 0m0.353s sys 0m0.450s % time gsi go.scm 10865
real 0m0.895s user 0m0.328s sys 0m0.394s % time gsi go.scm 10865
real 0m0.899s user 0m0.326s sys 0m0.387s
You can see that the second and third runs are faster than the first one, because the files have been loaded into the disk cache. When the number of .o1 files is lowered to 100 files the timing is:
% time gsi go.scm 10865
real 0m0.229s user 0m0.192s sys 0m0.034s
So it takes about one second of real time to load 36 MB of executable code distributed in 1000 .o1 files and about a quarter of a second of real time to load 3.6 MB of executable code distributed in 100 .o1 files.
I think that is reasonable.
Wouldn't launching incur a huge penalty in speed / space vs a selfcontained .exe as every .o1 is a full fleged dynamic library? Also, with this approach, distributing to my clients a folder containing 1000 .o1 files is everything but nice.
Why is that a problem? You just need to package your .o1 files in a .tar.gz and have a package installer that unpacks the archive at the appropriate place. In fact that's essentially how the Snow framework operates.
I could zip them together a bit like interpreted languages like Python do but then I don't think Windows or Gambit or ... can load .o1 files from a zip directly so I'd have to unzip them to a temp folder which is again not very tempting.
Any help / thoughts really appreciated. This is criticaly for my using Gambit for my project.
I don't really see a problem with this (either performance or convenience).
Marc
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.3 (Darwin)
iD8DBQFGcJY7//V9Zc2T/v4RAiL0AJ9QLgjly9E4r/W+NFctwnwQ4KfTQwCeM3DE bfN1+nv/IokEmLOBzGzmlh4= =lOk5 -----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 13-Jun-07, at 9:45 PM, Guillaume Cartier wrote:
Thanks Marc. These results are *very* interesting!
2 questions:
1- Will Gambit do as good a job in optimizing the code if I package my application as a bunch of .o1 files than if I had compiled them together in one executable?
Yes... if no declarations are used. If your code uses the declaration (declare (block)) then inter-procedural optimizations are possible (such as inlining) within that file. Of course if you want to be able to redefine functions (as seems to be the case for Jazz) then you cannot use this declaration.
There is a slight advantage with the single executable because the global variables can be statically resolved, but this is a very small factor in my experience.
2- If the answer to 1- is yes, then the only remaining thorn is to have to actually untar the .o1 files. Almost all modern languages like Python (.pyc), Java (.class), ... enable code distribution inside compressed archives which is really nice and easy for the developer and for the end user (it would annoy me if Mercurial upon install extracted it's 300+ .pyc files to my hard disk instead of keeping them in a neat library.zip file).
But what's the difference for the user in having the package in one "library.zip" file or in one *directory* "library" containing a bunch of .o1 files? This reminds me of the Mac OS format for applications. In Mac OS 9 an application is in a single specially formatted file which contains several "resources". In Mac OS X an application is in a hierarchically structured .app directory. So it seems the "trend" is away from specially formatted files!
Do you think something could be done to load the .o1 files directly from an archive? This could be a wonderful code distribution mechanism for Gambit too.
The problem is that the .o1 files are loaded with "dlopen" (Unix) and "LoadLibrary" (Windows), and these expect the file to be directly accessible on the file system. I am not aware of any workaround.
Marc
Marc Feeley wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 13-Jun-07, at 9:45 PM, Guillaume Cartier wrote:
Thanks Marc. These results are *very* interesting!
2 questions:
1- Will Gambit do as good a job in optimizing the code if I package my application as a bunch of .o1 files than if I had compiled them together in one executable?
Yes... if no declarations are used. If your code uses the declaration (declare (block)) then inter-procedural optimizations are possible (such as inlining) within that file. Of course if you want to be able to redefine functions (as seems to be the case for Jazz) then you cannot use this declaration.
In fact it's the opposite. A method in Jazz cannot be redefined, so I was planning on generating for methods something like (begin (declare (block)) (define (method-x) ...))
There is a slight advantage with the single executable because the global variables can be statically resolved, but this is a very small factor in my experience.
2- If the answer to 1- is yes, then the only remaining thorn is to have to actually untar the .o1 files. Almost all modern languages like Python (.pyc), Java (.class), ... enable code distribution inside compressed archives which is really nice and easy for the developer and for the end user (it would annoy me if Mercurial upon install extracted it's 300+ .pyc files to my hard disk instead of keeping them in a neat library.zip file).
But what's the difference for the user in having the package in one "library.zip" file or in one *directory* "library" containing a bunch of .o1 files? This reminds me of the Mac OS format for applications. In Mac OS 9 an application is in a single specially formatted file which contains several "resources". In Mac OS X an application is in a hierarchically structured .app directory. So it seems the "trend" is away from specially formatted files!
I totally aggree! In reality, it's all a question of what the end user perceives as "right" and "wrong" and this is much about trends, so hurray for MacOS X for setting up a directory based trend that is so much simpler for us developer :)
Do you think something could be done to load the .o1 files directly from an archive? This could be a wonderful code distribution mechanism for Gambit too.
The problem is that the .o1 files are loaded with "dlopen" (Unix) and "LoadLibrary" (Windows), and these expect the file to be directly accessible on the file system. I am not aware of any workaround.
Marc
Thanks again Marc and everyone else!
Guillaume
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 13-Jun-07, at 11:02 PM, Guillaume Cartier wrote:
Yes... if no declarations are used. If your code uses the declaration (declare (block)) then inter-procedural optimizations are possible (such as inlining) within that file. Of course if you want to be able to redefine functions (as seems to be the case for Jazz) then you cannot use this declaration.
In fact it's the opposite. A method in Jazz cannot be redefined, so I was planning on generating for methods something like (begin (declare (block)) (define (method-x) ...))
Let me explain what happens if you do this, so that you don't misunderstand the consequences.
In Gambit each global variable is implemented with two cells: the "value" cell and the "primitive" cell. The value cell is what is accessed when you mutate a global variable and reference it with no special declarations in effect. Both the value cell and the primitive cell are initialized when a toplevel procedure definition is evaluated *and* the (block) declaration is in effect. All of the predefined procedures (cons, append, make-thread, ...) are defined in the runtime library while the (block) declaration is in effect. The Gambit compiler uses this fact when compiling Scheme code while the (standard-bindings) and/or (extended-bindings) declarations are in effect. In this case a call to a predefined procedure will reference the primitive cell. This is faster because the compiler knows this is a procedure, and can avoid the procedure? check. Note that a call like (append X Y) in the context of a (standard-bindings) declaration will call the append procedure in the runtime library even if the user has mutated the append variable with a set! .
The (block) declaration also allows the compiler to perform optimizations within the file because the value attached to a variable is known not to change (unless there is a set! within the file). This allows copy propagation, function inlining, etc at the file level. So a file containing this code:
(declare (block)) (define n 10) (define (f x) (* x x)) (define (g) (f (+ n 1)))
will be compiled as though it was:
(define n 10) (define (f x) (* x x)) (define (g) (let ((x (+ 10 1))) (* x x)))
So you have to be aware that the (block) declaration causes linking optimizations at the file level. So it is OK to use the (block) declaration if the granularity of "redefinition" is the file, but it is not OK if you want to have the ability to redefine individual procedures. After loading the above (compiled) file, a (define (f x) (* x 2)) at the REPL or in another file that you load will not affect the behavior of a call to g. The only way to "update" g is to redefine it.
Note also that if another file does a (define h g), and then you reload the above file with new definitions for f and g, you will access the original definition of g when you call h. In other words, reloading a file compiled with the (block) declaration changes the content of the value and primitive cells, it does not "overwrite", purge or invalidate the previous machine code.
Marc