Dear all, I am new to Gambit-C. I keep hearing that with Gambit-C, it is easy to create small executable. But when I tried, a simple hello world program takes around 3 MB when compiled as a STANDALONE executable. Is there a way to create small executable which includes only used functions, just like in C, we don't link the whole library into the executable.
Thanks.
Peter
Yahoo!香港提供網上安全攻略,教你如何防範黑客! 請前往 http://hk.promo.yahoo.com/security/ 了解更多!
Afficher les réponses par date
2009/8/17 peter lo peter19852001@yahoo.com.hk:
Dear all, I am new to Gambit-C. I keep hearing that with Gambit-C, it is easy to create small executable. But when I tried, a simple hello world program takes around 3 MB when compiled as a STANDALONE executable. Is there a way to create small executable which includes only used functions, just like in C, we don't link the whole library into the executable. Thanks. Peter
Hi Peter.
This is quite hard, because in Scheme, you may call a function which does not appear in the source code. For example, the code below uses the function "LOAD", but there is no way you can forsee that… This is not a problem of Gambit-C, it's a feature of Scheme (and other languages with an EVAL function)
(define func "lo") (define tion "ad") (eval (list (string->symbol (string-append func tion)) "another-file.scm"))
P!
Dear Adrien Piérard, Thanks for the reply. I understand that the dynamic features of Scheme makes it difficult to leave out some of the functions. What if the user is willing to sacrifice some dynamic features (such as eval)? Is there any plan in such a direction? Suppose we divide all of the functions of Gambit-C Scheme into different modules, possibly with hierarchies. Then for a particular program, if certain functions are not used, the whole module need not be linked. In this way, we can add many libraries to Scheme without worrying inflating standalone executable unnecessarily.
Regards, Peter
----- 郵件原件 ---- 寄件人﹕ Adrien Piérard pierarda@iro.umontreal.ca 收件人 peter lo peter19852001@yahoo.com.hk 副本(CC) gambit-list@iro.umontreal.ca 傳送日期﹕ 2009 年 8月 17 日 星期一 下午 1:59:04 主題: Re: [gambit-list] How to create small standalone executable with Gambit-C?
2009/8/17 peter lo peter19852001@yahoo.com.hk:
Dear all, I am new to Gambit-C. I keep hearing that with Gambit-C, it is easy to create small executable. But when I tried, a simple hello world program takes around 3 MB when compiled as a STANDALONE executable. Is there a way to create small executable which includes only used functions, just like in C, we don't link the whole library into the executable. Thanks. Peter
Hi Peter.
This is quite hard, because in Scheme, you may call a function which does not appear in the source code. For example, the code below uses the function "LOAD", but there is no way you can forsee that… This is not a problem of Gambit-C, it's a feature of Scheme (and other languages with an EVAL function)
(define func "lo") (define tion "ad") (eval (list (string->symbol (string-append func tion)) "another-file.scm"))
P!
From three minutes searching the main list:
https://webmail.iro.umontreal.ca/pipermail/gambit-list/2008-October/ 002713.html
and following.
Brad
Dear Brad, Thanks for the link, I didn't notice that. After reading those post, I feel that there is general interest in reducing executable size, yet the current solution may not be satisfactory. I really suggest some smart guy can work on this to make Gambit-C more attractive as an application programming language. For the time being, I think the quick solution is to use shared library. Is there anyone kind enough to write a how-to on creating and distributing small executable? I guess on Linux this is not difficult, but does this strategy work easily on Windows (which is the platform I happen to be working on, and honestly when we intend to distribute executables, we do not want to leave out users using Windows.)?
Regards, Peter
----- 郵件原件 ---- 寄件人﹕ Bradley Lucier lucier@math.purdue.edu 收件人 peter lo peter19852001@yahoo.com.hk 副本(CC) Bradley Lucier lucier@math.purdue.edu; Adrien Piérard pierarda@iro.umontreal.ca; gambit-list@iro.umontreal.ca 傳送日期﹕ 2009 年 8月 17 日 星期一 下午 7:50:11 主題: Re: [gambit-list] Re: How to create small standalone executable with Gambit-C?
From three minutes searching the main list:
https://webmail.iro.umontreal.ca/pipermail/gambit-list/2008-October/002713.h...
and following.
Brad
Yahoo!香港提供網上安全攻略,教你如何防範黑客! 請前往 http://hk.promo.yahoo.com/security/ 了解更多!
2009/8/18 peter lo peter19852001@yahoo.com.hk:
and honestly when we intend to distribute executables, we do not want to leave out users using Windows.)?
It hurts me to say that, but size really isn't important anymore, *especially* when targetting desktop OS's like Windows, is it?r
On the other hand, it would be welcome to have a flavor of Gambit which is less expressive. I do not know how burdensome it would be to be able to prune whole parts of gambit to create "non development-ready" applications (that is, once the final product is to be delivered, compile it with a version of gambit which does not embed the whole runtime, does not allow debugging, etc). Or, maybe, allow the user to explicitely tell what he needs to import
(declare (with-great-power-comes-great-responsability ;; EVAL's environment should only know those, should it be used read current-input-port current-output-port char=? list + - /*))
(define (arithmetic-evaluator) ...)
(eval '(car '(1 2))) ;; segfault. You should've added CAR too. Don't complain to us.
Even though this is far from complete, this may be a compromise. (And this is in favor of giving a seconde parameter to EVAL, if the user has the capacity to create environments).
So, forgiving the fact that I am not an expert language implementer, how realistic are those ideas for Gambit-C?
P!
I agree that on modern systems size is not that important anymore, but it is unpleasant to have an executable of serveral MBs when you know that the same program written in another language will be only a few hundred KBs.
On the other hand, it would be welcome to have a flavor of Gambit which is less expressive. I do not know how burdensome it would be to be able to prune whole parts of gambit to create "non development-ready" applications (that is, once the final product is to be delivered, compile it with a version of gambit which does not embed the whole runtime, does not allow debugging, etc). Or, maybe, allow the user to explicitely tell what he needs to import
I support this idea. As I have suggested in a previous mail, we should aim at making Gambit-C as modular as possible. For example, the system may be a bunch of modules: -- basic runtime -- numbers -- full numeric tower support -- characters and strings -- various data structures like hash table, vectors,... -- networking -- SRF1 -- SRF2 and so on
The above partition of the modules is only a random suggestion, but you get the idea. With such a system, only the relevant parts get into the final program, so we pay for only when we need.
Regards, Peter
----- 郵件原件 ---- 寄件人﹕ Adrien Piérard pierarda@iro.umontreal.ca 收件人 peter lo peter19852001@yahoo.com.hk 副本(CC) Bradley Lucier lucier@math.purdue.edu; Gambit Mailing List gambit-list@iro.umontreal.ca 傳送日期﹕ 2009 年 8月 18 日 星期二 上午 11:01:24 主題: Re: [gambit-list] Re: Re: How to create small standalone executable with Gambit-C?
2009/8/18 peter lo peter19852001@yahoo.com.hk:
and honestly when we intend to distribute executables, we do not want to leave out users using Windows.)?
It hurts me to say that, but size really isn't important anymore, *especially* when targetting desktop OS's like Windows, is it?r
On the other hand, it would be welcome to have a flavor of Gambit which is less expressive. I do not know how burdensome it would be to be able to prune whole parts of gambit to create "non development-ready" applications (that is, once the final product is to be delivered, compile it with a version of gambit which does not embed the whole runtime, does not allow debugging, etc). Or, maybe, allow the user to explicitely tell what he needs to import
(declare (with-great-power-comes-great-responsability ;; EVAL's environment should only know those, should it be used read current-input-port current-output-port char=? list + - /*))
(define (arithmetic-evaluator) ...)
(eval '(car '(1 2))) ;; segfault. You should've added CAR too. Don't complain to us.
Even though this is far from complete, this may be a compromise. (And this is in favor of giving a seconde parameter to EVAL, if the user has the capacity to create environments).
So, forgiving the fact that I am not an expert language implementer, how realistic are those ideas for Gambit-C?
P!
On 17-Aug-09, at 11:32 PM, peter lo wrote:
The above partition of the modules is only a random suggestion, but you get the idea. With such a system, only the relevant parts get into the final program, so we pay for only when we need.
Here's an idea that is based on
https://webmail.iro.umontreal.ca/pipermail/gambit-list/2008-October/002725.h...
It involves rewriting the Scheme library, which is not as complex as one might think, for example the BIT system contains a R4RS library written in Scheme that could be reused with only a few changes.
You need to first create a minimal Gambit library that only contains the kernel:
% ./configure % make % make bootstrap % ... now edit lib/makefile.in to only include _kernel in the library... % diff lib/makefile.in lib/makefile.in-orig 78,83c78,89 < MODULES = _kernel < MODULES_SCM = _kernel.scm < MODULES_C = _kernel.c < MODULES_O = _kernel@obj@ < MODULES_O_PLUS = +_kernel@obj@ < MODULES_O_COMMA = _kernel@obj@ ---
MODULES = _kernel _system _num _std \ _eval _io _nonstd _thread _repl MODULES_SCM = _kernel.scm _system.scm _num.scm _std.scm \ _eval.scm _io.scm _nonstd.scm _thread.scm _repl.scm MODULES_C = _kernel.c _system.c _num.c _std.c \ _eval.c _io.c _nonstd.c _thread.c _repl.c MODULES_O = _kernel@obj@ _system@obj@ _num@obj@ _std@obj@ \ _eval@obj@ _io@obj@ _nonstd@obj@ _thread@obj@ _repl@obj@ MODULES_O_PLUS = +_kernel@obj@ +_system@obj@ +_num@obj@ +_std@obj@ \ +_eval@obj@ +_io@obj@ +_nonstd@obj@ +_thread@obj@ +_repl@obj@ MODULES_O_COMMA = _kernel@obj@,_system@obj@,_num@obj@,_std@obj@,\ +_eval@obj@,_io@obj@,_nonstd@obj@,_thread@obj@,_repl@obj@
87c93,94 < MODULES_O_IN_COMPILE_ORDER = _kernel@obj@ ---
MODULES_O_IN_COMPILE_ORDER = _io@obj@ _num@obj@ _std@obj@ \ _kernel@obj@ _nonstd@obj@ _repl@obj@ _eval@obj@ _thread@obj@
_system@obj@ % cd lib % make clean % make % cd .. % ./gsc-comp -:=. -exe test.scm % strip test % ls -l test -rwxr-xr-x 1 feeley feeley 308672 Aug 18 01:35 test % ./test 10946
The file test.scm is attached below. As you can see I obtained a 300kB executable for this program.
The program could have included all of the Scheme library. Note that the definitions of the library procedures are in a "let" so the Gambit compiler can eliminate all of the definitions that are not needed. It is in effect a whole-program compilation.
What would be really nice is if this could be done with the current Gambit runtime library. This would require some changes to the structure of the library. If anyone is interested in doing this, please let me know!
Marc
;; File: "test.scm"
(declare (standard-bindings) (extended-bindings) (not inline-primitives) (inlining-limit 0) (not safe) (fixnum) (block) )
(c-declare "#include <stdio.h>") ;; to access puts
(let ()
(define puts (c-lambda (char-string) int "puts"))
(define (make-string k #!optional (f #\space)) (##make-string k f))
(define (substring-move! src-str src-start src-end dst-str dst-start) ;; Copy direction must be selected in case src-str and ;; dst-str are the same string. (if (fx< src-start dst-start) (let loop1 ((i (fx- src-end 1)) (j (fx- (fx+ dst-start (fx- src-end src-start)) 1))) (if (fx< i src-start) dst-str (begin (string-set! dst-str j (string-ref src-str i)) (loop1 (fx- i 1) (fx- j 1))))) (let loop2 ((i src-start) (j dst-start)) (if (fx< i src-end) (begin (string-set! dst-str j (string-ref src-str i)) (loop2 (fx+ i 1) (fx+ j 1))) dst-str))))
(define (substring str start end) (substring-move! str start end (make-string (fxmax (fx- end start) 0)) 0))
(define (append-strings lst) (let loop1 ((n 0) (lst1 lst) (lst2 '())) (if (pair? lst1) (let ((str (car lst1))) (loop1 (fx+ n (string-length str)) (cdr lst1) (cons str lst2))) (let ((result (make-string n))) (let loop2 ((n n) (lst2 lst2)) (if (pair? lst2) (let* ((str (car lst2)) (len (string-length str)) (new-n (fx- n len))) (substring-move! str 0 len result new-n) (loop2 new-n (cdr lst2))) result))))))
(define (string-append . lst) (append-strings lst))
(define (append x y) (if (pair? x) (cons (car x) (append (cdr x) y)) y))
(define (fold kons knil lst) (let loop ((lst lst) (ans knil)) (if (pair? lst) (loop (cdr lst) (kons (car lst) ans)) ans)))
(define (number->string n #!optional (b 10)) (if (fx< n b) (substring "0123456789abcdef" n (fx+ n 1)) (string-append (number->string (fxquotient n b) b) (number->string (fxmodulo n b) b))))
(define (display obj) (cond ((string? obj) (puts obj)) ((fixnum? obj) (puts (number->string obj))) (else (puts "<unknown object>"))))
;; ... put rest of Scheme library here ... ;; possibly a (include "scheme-lib.scm") for the whole thing
;; main program:
(define (fib n) (if (fx< n 2) 1 (fx+ (fib (fx- n 1)) (fib (fx- n 2)))))
(display (fib 20)) )
Adrien Piérard wrote:
This is quite hard, because in Scheme, you may call a function which does not appear in the source code. For example, the code below uses the function "LOAD", but there is no way you can forsee that… This is not a problem of Gambit-C, it's a feature of Scheme (and other languages with an EVAL function)
(define func "lo") (define tion "ad") (eval (list (string->symbol (string-append func tion)) "another-file.scm"))
Sure. If you call eval then everything else has to get dragged in. If you don't call eval, why does the entire library get linked in rather than only what's called?
-Dave
Hallo,
On 8/17/09, David Bridgham dab@froghouse.org wrote:
Sure. If you call eval then everything else has to get dragged in. If you don't call eval, why does the entire library get linked in rather than only what's called?
This is not so easy to spot:
(define my-func eval) ,,, (if some-condition-entered-by-the-user (begin (set! my-func println) ...) ... .. (my-func (+ 1 2 3 4)
Cheers,
On 17-Aug-09, at 7:45 AM, David Bridgham wrote:
Adrien Piérard wrote:
This is quite hard, because in Scheme, you may call a function which does not appear in the source code. For example, the code below uses the function "LOAD", but there is no way you can forsee that… This is not a problem of Gambit-C, it's a feature of Scheme (and other languages with an EVAL function)
(define func "lo") (define tion "ad") (eval (list (string->symbol (string-append func tion)) "another- file.scm"))
Sure. If you call eval then everything else has to get dragged in. If you don't call eval, why does the entire library get linked in rather than only what's called?
Tree-shaking in a Lisp-like language is not easy...
(##global-var-ref (##make-global-var (string->symbol (string-append
"ev" "al")))) #<procedure #2 eval>
Also, understand that something as "simple" as the call (write "hello") is not so simple. The write procedure needs lots of runtime support procedures to do its work. For example, write uses hash tables to detect circular structures, it uses most of the I/O infrastructure including the Unicode character encoding support, it uses string ports for handling the special case (write (list 'unquote '@foo)), etc.
Moreover, Gambit was designed to support live debugging. I view live debugging as one of the most important legacies of Lisp. If the (write "hello") call encounters an error (such as "disk full") then the debugger will be invoked to allow the programmer to understand and fix the problem. Gambit's debugger is a REPL where you can evaluate any expression. So you need to have "eval", "load", and all the rest of the Gambit runtime system available.
Notice also that you can setup Gambit so that the runtime library will be a shared library roughly 3MB big. A "hello world" program will be a few kB. The Gambit library is mostly self contained. It needs the C library (for basic things like malloc and sqrt and very little else) so the total "size" of the runtime is actually much less than for a language like Java.
If you wish to try to get a minimal runtime system for Gambit, check:
https://webmail.iro.umontreal.ca/pipermail/gambit-list/2008-October/002725.h...
Marc
Date: Mon, 17 Aug 2009 09:35:04 -0400 From: Marc Feeley feeley@iro.umontreal.ca
Tree-shaking in a Lisp-like language is not easy...
> (##global-var-ref (##make-global-var (string->symbol (string-append "ev" "al")))) #<procedure #2 eval>
This is so only if one insists upon having such global databases at run-time. Nothing prevents a Scheme implementation from using other mechanisms. In Scheme48, the compiler, not the run-time system, knows about environments. You can build a system that includes the compiler and run-time system and knows about its own environments (called reifying the system), or you can build a system that includes only the run-time system, without the compiler or environments; then any unused parts of the run-time system are omitted. If you use EVAL, and also use, say, SCHEME-REPORT-ENVIRONMENT, but don't reify your own program's environments, then the compiler and only the environments containing the bindings of the RnRS are included -- no other environments need be.
An environment is a compiler data structure, not a run-time data structure -- a map from names to meanings, which for variables describe the location where the variable's value is stored at run-time. The locations can exist at run-time in the absence of the environment data structure that the compiler used. Procedures such as Gambit's ##MAKE-GLOBAL-VAR are not necessary to make Scheme work.
On 17-Aug-09, at 10:17 AM, Taylor R Campbell wrote:
Procedures such as Gambit's ##MAKE-GLOBAL-VAR are not necessary to make Scheme work.
You are correct. Scheme can be made to work with very little code. For example the PICBIT system which implements most of R4RS can fit in roughly 20kb of code, and the PICOBIT VM fits in less than 10kb of code. Gambit's design goals do not include "a compact runtime system". Gambit was designed to be portable and efficient, and to provide a superior development and production environment for Scheme.
In my message I was explaining that in Lisp-like languages, such as Gambit Scheme, where dynamism is pervasive and features are highly interdependent, it is hard to implement an effective tree-shaker.
Are you saying that Scheme48 does not adhere to that Lisp philosophy? I thought Scheme48 supported "live debugging" and that it was the default execution mode. Does the debugger rely on eval? What happens when an exception is raised at run time? On my MacBook Pro I checked the size of the runtime system of Scheme48 and Gambit and there is in fact very little difference (scheme48.image=2.7MB and gsi=3.3MB). Also, when executing (let loop () (loop)) the ps program reveals that Scheme48 has a RSS of 6.4MB and Gambit has a RSS of 2.4MB. So at execution time, for a small program, Scheme48 seems to take 2.5 times as much memory as Gambit.
Marc
On 17-Aug-09, at 11:04 AM, Marc Feeley wrote:
Also, when executing (let loop () (loop)) the ps program reveals that Scheme48 has a RSS of 6.4MB and Gambit has a RSS of 2.4MB. So at execution time, for a small program, Scheme48 seems to take 2.5 times as much memory as Gambit.
Sorry to reply to my own post, but I checked the RSS for the Java "Hello World" program, and it is 11.5MB or over 4 times as much as Gambit. Moreover, it takes 0.015 seconds to execute the Gambit "Hello World" program (and slightly more for Scheme48, i.e. 0.023 seconds), and 10 times longer for the Java program. So I think Gambit's executable size, memory use and startup time are quite competitive with other systems.
Marc
The "mainstream" computer at my house with the least memory (a four-year-old iMac G5) has 1.5GB of memory, so a 3MB "footprint" for Gambit is about .2% of the memory of the machine.
I have a few Gumstix computers lying around (about the size of my longest finger) that have 64MB of memory, so 3MB is about 5% of the total RAM.
I can't say that making Gambit's minimal memory requirements tremendously smaller is a high priority of mine.
Brad
2009/8/17 Bradley Lucier lucier@math.purdue.edu:
The "mainstream" computer at my house with the least memory (a four-year-old iMac G5) has 1.5GB of memory, so a 3MB "footprint" for Gambit is about .2% of the memory of the machine.
I have a few Gumstix computers lying around (about the size of my longest finger) that have 64MB of memory, so 3MB is about 5% of the total RAM.
I can't say that making Gambit's minimal memory requirements tremendously smaller is a high priority of mine.
But quite a lot of people are interested in using Gambit on mobile devices with very limited memory and/or filesystem space (e.g. iPhone, or my old Palm T3, although that was asking a bit too much, having to compile to 68k, small section code even though the machine was ARM-based :/).
So if there was a relatively painless way to get all the lovely efficiency and other benefits of modern Gambit, but cut away some unneeded stuff for deployment to low memory platforms, that would be very sexy indeed. I'd try to do it, if I wasn't rubbish at Scheme :D
Oisín
Date: Mon, 17 Aug 2009 11:04:26 -0400 From: Marc Feeley feeley@iro.umontreal.ca
Are you saying that Scheme48 does not adhere to that Lisp philosophy? I thought Scheme48 supported "live debugging" and that it was the default execution mode. Does the debugger rely on eval? What happens when an exception is raised at run time?
The REPL (or `command processor', `development environment', &c.) is a program just like any other. The REPL uses EVAL, and knows about the environments of (some of) Scheme48's internals, including the REPL's environments (i.e. the environments in which the REPL's source code were compiled) -- that is, the REPL is reified. If you run your programs under the REPL, it will establish condition handlers that cause the debugger to be entered, and all the environments of your program, and any libraries in Scheme48, will be available. But the REPL and the database of your program's global variables aren't built-in to the compiler or to the virtual machine or to the run-time system -- you get what you ask for. If you make your program not to run under the REPL (and don't establish condition handlers that will enter the REPL), then you won't get the REPL.
The point isn't that Scheme48 fails to be adhere to any philosophy of Lisp -- the point is that Scheme48 is a configurable system of parts, not a monolith of global databases as many other Scheme systems are, and as Common Lisp is mandated to be. Fortunately, Scheme is not mandated thus. So for Scheme48, shrinking images is not a matter of going to the extra effort of walking through the global databases and destroying links that you don't want (`tree-shaking'); it's only a matter of specifying what you do want, and then anything not required by that will be omitted anyway.
On my MacBook Pro I checked the size of the runtime system of Scheme48 and Gambit and there is in fact very little difference (scheme48.image=2.7MB and gsi=3.3MB). Also, when executing (let loop () (loop)) the ps program reveals that Scheme48 has a RSS of 6.4MB and Gambit has a RSS of 2.4MB. So at execution time, for a small program, Scheme48 seems to take 2.5 times as much memory as Gambit.
If you evaluate (LET LOOP () (LOOP)) in the REPL, all of the REPL and libraries will remain in the image. If you build a new image that only evaluates (LET LOOP () (LOOP)), it will be much smaller:
,build (lambda (arguments) (let loop () (loop))) foo.image
% ls -lh foo.image -rw-r--r-- 1 riastradh riastradh 740K Aug 17 12:15 foo.image
The 740K consists mostly of the run-time system, which is initialized in images created with the ,BUILD command -- starting the thread scheduler, enabling interrupts in the virtual machine, establishing the dynamic environment for the current input and output ports, &c. This can be omitted, too:
,open write-images (write-image "bar.image" (lambda arguments (let loop () (loop))) "")
% ls -lh bar.image -rw-r--r-- 1 riastradh riastradh 14K Aug 17 12:19 bar.image
(There is still a bit of extra gunk in this image due to the FFI. Before Scheme48's current FFI, bar.image would have been even smaller; e.g., in Scheme48 0.36, the above call yields a baz.image of only 148 bytes.)
How much heap space Scheme48 allocates is a run-time option which doesn't change by default (unless the image is too large for the default heap space). And of course Scheme48 is based on a virtual machine, all of which is loaded into memory; in this respect Gambit has a theoretical advantage, because there is no (run-time) virtual machine needed to interpret its compiled code.
On 17-Aug-09, at 10:17 AM, Taylor R Campbell wrote:
You can build a system that includes the compiler and run-time system and knows about its own environments (called reifying the system), or you can build a system that includes only the run-time system, without the compiler or environments; then any unused parts of the run-time system are omitted.
I'm curious to know how this works. If you have a module which implements several procedures, say the srfi-1 list library, and you use only one of the self-contained procedures, say "xcons", will the Scheme48 compiler or linker eliminate the rest of the code of srfi-1? If so, how does it do that? What is the bytecode representation of the compiled library?
Marc
Date: Mon, 17 Aug 2009 17:19:30 -0400 From: Marc Feeley feeley@iro.umontreal.ca
I'm curious to know how this works. If you have a module which implements several procedures, say the srfi-1 list library, and you use only one of the self-contained procedures, say "xcons", will the Scheme48 compiler or linker eliminate the rest of the code of srfi-1? If so, how does it do that? What is the bytecode representation of the compiled library?
For each global variable, an object called a location is allocated. Locations have a little bit of structure, but primarily they have a field for the value of the global variable. The representation of procedures has two parts: an environment and a pointer to a /template/. The environment holds the values, or cells for the values, of local variables. The template is the static part of the procedure: it holds the byte code, the literals, the locations for global variables, and the templates for subprocedures, corresponding to the lambdas that require heap allocation. Templates are thus arranged into a tree like the tree of contours in the program, and parents can be garbage- collected if they are no longer used.
Compiling a package is similar to compiling a big lambda containing the forms in the package's source code, with the side effect of filling a table mapping names to the meanings defined in the source code. The procedure (and its template) thereby obtained can be garbage-collected once it has been used, since nothing else will have references to it.
If you write a program that uses XCONS, but nothing else defined in the SRFI 1 package, then your program will remember the location for with the global variable XCONS, which will remember the procedure that was stored in the location when SRFI 1 was loaded, which will remember all its own literals and locations and subtemplates, but nothing else of the SRFI 1 package.
Also in Scheme48, if you are using the command processor, you can type `,go <expression>', and most of the execution of Scheme48 will be tail-recursively replaced by that of <expression> -- so that the command processor can be garbage-collected. (There is a little bit of the run-time system left on the stack, but that's all.)
Naturally, Gambit can't do quite this, if you want a single C object or library per unit of source code (i.e., per file, in Gambit's case), because you can't usefully garbage-collect a portion of an object you have dlopened or statically linked; dlclose works at the granularity of entire objects, and what is statically linked can't be eliminated. But that's unrelated to whether one can just do (##GLOBAL-VAR-REF (##MAKE-GLOBAL-VAR ...)) in a `Lisp-like language'. (In fact, it's a little more closely related to whether one can do *((foo_t *) dlsym (RTLD_DEFAULT, "foo")) in C...)
Hallo,
On 8/17/09, Adrien Piérard pierarda@iro.umontreal.ca wrote:
This is quite hard, because in Scheme, you may call a function which does not appear in the source code. For example, the code below uses the function "LOAD", but there is no way you can forsee that… This is not a problem of Gambit-C, it's a feature of Scheme (and other languages with an EVAL function)
I installed Quantz for Mac OS X this weekend and the executable is just 24kB. I could not find a Gambit framework in the application bundle, nor in /Library. I wonder how they did that...
I installed Quantz for Mac OS X this weekend and the executable is just 24kB. I could not find a Gambit framework in the application bundle, nor in /Library. I wonder how they did that...
Well, I do not know the background of the programmers, but as I have been very close to the "demoscene", there are some tricks I know: - use a home made linker - use a (de)compressor - fork the compiler to keep only what you need (which *can* be an option if you have time for that, and will get money back too) - hide the framework in the README.TXT (yeah, that's cheating…) - check if the framework is not already on the system prior to deciding whether you install it or not…
By the way, if you're on a mac, you should be able to ltrace, ktrace and strace the program. If the framework is somewhere else, you'll find it quickly I guess. Just be sure that {l,s,k}tracing the binary is legal (I don't know if that's considered debugging and/or reverse engineering).
P!
Alex Queiroz wrote:
Hallo,
On 8/17/09, Adrien Piérard pierarda@iro.umontreal.ca wrote:
This is quite hard, because in Scheme, you may call a function which does not appear in the source code. For example, the code below uses the function "LOAD", but there is no way you can forsee that… This is not a problem of Gambit-C, it's a feature of Scheme (and other languages with an EVAL function)
I installed Quantz for Mac OS X this weekend and the executable
is just 24kB. I could not find a Gambit framework in the application bundle, nor in /Library. I wonder how they did that...
It is really easy. You just need the use Guillaume Germain's magical wand...
David
On Mon, Aug 17, 2009 at 10:17 AM, David St-Hilaire < sthilaid@iro.umontreal.ca> wrote:
Alex Queiroz wrote:
Hallo,
I installed Quantz for Mac OS X this weekend and the executable
is just 24kB. I could not find a Gambit framework in the application bundle, nor in /Library. I wonder how they did that...
It is really easy. You just need the use Guillaume Germain's magical wand...
Argh.
It's just a small executable that will load an encrypted dynamic library that contains everything. Please don't ask me about it.
Guillaume
Hallo,
On 8/17/09, Guillaume Germain guillaume.germain@gmail.com wrote:
It's just a small executable that will load an encrypted dynamic library that contains everything. Please don't ask me about it.
Thanks for the explanation.
Cheers,
On 17-Aug-09, at 1:30 AM, peter lo wrote:
Dear all, I am new to Gambit-C. I keep hearing that with Gambit-C, it is easy to create small executable. But when I tried, a simple hello world program takes around 3 MB when compiled as a STANDALONE executable. Is there a way to create small executable which includes only used functions, just like in C, we don't link the whole library into the executable.
Thanks.
Peter
It depends what you mean by a "standalone" executable. Do you include the shared libraries in the program's size? Do you include the operating system, which implements the low-level side of the libraries?
I assume you are interested in how much space is required on the file system to store the executable. Here's a simple experiment on Mac OS X of compiling a hello-world program in Scheme and C. Gambit is setup to use shared libraries, which is fair since that's what the C compiler normally does when it compiles and links a C program.
% cd gambc-v4_5_1-devel % ./configure --enable-shared --enable-single-host --prefix=/Users/ feeley/g % make % make install % cat hello-world.scm (println "Hello World") % ~/g/bin/gsc -exe hello-world.scm % ./hello-world Hello World % otool -L hello-world hello-world: /Users/feeley/g/lib/libgambc.dylib (compatibility version 0.0.0, current version 0.0.0) /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.1.4) % ls -l hello-world /Users/feeley/g/lib/libgambc.dylib /usr/lib/ libgcc_s.1.dylib /usr/lib/libSystem.B.dylib -rw-r--r-- 1 feeley feeley 4316808 Aug 17 16:39 /Users/feeley/g/lib/ libgambc.dylib -r-xr-xr-x 1 root wheel 7895472 Apr 1 01:45 /usr/lib/ libSystem.B.dylib -rw-r--r-- 1 root wheel 264016 Oct 6 2007 /usr/lib/libgcc_s. 1.dylib -rwxr-xr-x 1 feeley feeley 13124 Aug 17 16:47 hello-world % gcc -shared hello-world.c % otool -L a.out a.out: /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.1.4) % ls -l a.out /usr/lib/libgcc_s.1.dylib /usr/lib/libSystem.B.dylib -r-xr-xr-x 1 root wheel 7895472 Apr 1 01:45 /usr/lib/ libSystem.B.dylib -rw-r--r-- 1 root wheel 264016 Oct 6 2007 /usr/lib/libgcc_s. 1.dylib -rwxr-xr-x 1 feeley feeley 12588 Aug 17 16:49 a.out
So the actual executables (hello-world and a.out) are almost the same size on the file system. They both use some of the system's shared libraries, in particular libSystem.B.dylib which is almost 8MB. The Gambit program also links with Gambit's runtime library, libgambc.dylib, which is 4MB. So even if you include the shared libraries in the program's "size", both programs are reasonably similar in size (8MB vs 12MB).
Marc