Just some opinions.
asmjs.org defines a portable subset that allows JS platforms to compile into very fast code. Targeting that subset, and within their spec, is probably a good idea.
JS has, and is becoming increasingly, a very, very important platform. With ajax and rest services, code increasingly independent from the back-end is being developed. So, in a very important sense, JS has become its own platform, just like X86, and X86_64, along with Linux, Windows, Mac, etc.
Many apps consist of two major parts: the back-end processing, and the front-end human interface. While one can write the back-end processing in any of dozens of languages targeting X86 or a VM, and the OS, there is really only one target for the other half - the human interface - and that is JS.
While many languages are now targeting the JS platform (I am using the word "platform" to mean X86, X86_64, either, plus the OS) including, believe it or not, Smalltalk, there are few that can, IMO, be used in a real world application for several reasons. But, these issues will likely be resolved soon.
With respect, Gambit, at least in the past, was in many ways another toy solution - one with a functioning "Hello world" application but missing important features that make it usable in a real-world situation. I would love to be a part of that solution, but alas, life doesn't offer me that level of freedom. I am only capable of using a system that is reportedly working, reporting bugs, making small adjustments, and providing feedback. Regardless of my attitude, preferences, and intentions, I cannot provide more.
Look at node (JS for the back-end) and its vastly increasing popularity. Since developers are being forced to use JS on the front-end, it's bleeding over to the back-end. I think solutions that take JS seriously at this stage will prosper greatly.
Now, having said all that, I would love to see Gambit target JS as a high-priority, first-class target. If that were the case, I would be happy to contribute what I stated above.
Thanks!
Blake McBride
Afficher les réponses par date
Some other good links:
http://www.infoq.com/news/2015/02/ie-chrome-asmjs
https://en.wikipedia.org/wiki/Asm.js#Adoption
https://hacks.mozilla.org/2015/03/asm-speedups-everywhere
Also,
https://medium.com/javascript-scene/what-is-webassembly-the-dawn-of-a-new-er...
On Fri, Jan 1, 2016 at 10:44 AM, Blake McBride blake@mcbride.name wrote:
Just some opinions.
asmjs.org defines a portable subset that allows JS platforms to compile into very fast code. Targeting that subset, and within their spec, is probably a good idea.
JS has, and is becoming increasingly, a very, very important platform. With ajax and rest services, code increasingly independent from the back-end is being developed. So, in a very important sense, JS has become its own platform, just like X86, and X86_64, along with Linux, Windows, Mac, etc.
Many apps consist of two major parts: the back-end processing, and the front-end human interface. While one can write the back-end processing in any of dozens of languages targeting X86 or a VM, and the OS, there is really only one target for the other half - the human interface - and that is JS.
While many languages are now targeting the JS platform (I am using the word "platform" to mean X86, X86_64, either, plus the OS) including, believe it or not, Smalltalk, there are few that can, IMO, be used in a real world application for several reasons. But, these issues will likely be resolved soon.
With respect, Gambit, at least in the past, was in many ways another toy solution - one with a functioning "Hello world" application but missing important features that make it usable in a real-world situation. I would love to be a part of that solution, but alas, life doesn't offer me that level of freedom. I am only capable of using a system that is reportedly working, reporting bugs, making small adjustments, and providing feedback. Regardless of my attitude, preferences, and intentions, I cannot provide more.
Look at node (JS for the back-end) and its vastly increasing popularity. Since developers are being forced to use JS on the front-end, it's bleeding over to the back-end. I think solutions that take JS seriously at this stage will prosper greatly.
Now, having said all that, I would love to see Gambit target JS as a high-priority, first-class target. If that were the case, I would be happy to contribute what I stated above.
Thanks!
Blake McBride
Aside from Emscripten I'm not aware of any other compilers that target asm.js, do you know of any? Web assembly looks quite interesting but its far too experimental to even consider for anything at this stage.
My 9-5 is in a web dev shop and I've never seen any (asm.js / pnacl / etc) in the wild aside from a couple of 3d game engines ported to run in browser. Its certainly not part of the typical web development ecosystem.
I'm not sure what you consider non-toy like but there are certainly plenty of robust options from typed/untyped javascript variants through to completely different languages like ocaml, clojurescript, haskell etc.
But as someone who really wants to make use of the Gambit javascript backend on an upcoming commercial project I'd certainly appreciate hearing of any issues you have encountered so far.
Cheers,
James
On Sat, Jan 2, 2016 at 5:24 AM, Blake McBride blake@mcbride.name wrote:
Also see: *MailScanner soupçonne le lien suivant d'être une tentative de fraude de la part de "en.wikipedia.org" * https://en.wikipedia.org/wiki/Emscripten https://en.wikipedia.org/wiki/Emscripten
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
Hi,
On Fri, Jan 1, 2016 at 10:36 PM, James Baker cycle.code.media@gmail.com wrote:
Aside from Emscripten I'm not aware of any other compilers that target asm.js, do you know of any?
WebAssembly does now too. I think, perhaps, that there may be some effort to use it by hand in some instances as well.
Web assembly looks quite interesting but its far too experimental to even consider for anything at this stage.
Perhaps, but that may not be the case for asm.js.
My 9-5 is in a web dev shop and I've never seen any (asm.js / pnacl / etc) in the wild aside from a couple of 3d game engines ported to run in browser. Its certainly not part of the typical web development ecosystem.
True, but:
A. Most web apps are so simple (given the current relative speed of modern JS in the browser) that it doesn't matter.
B. Most people aren't writing in Scheme and compiling to JS either. Doing that would be because the app is significantly more sophisticated than a currently typical app - for example a video game or a full-fledged business app (like mine - arahant.com ).
I'm not sure what you consider non-toy like but there are certainly plenty of robust options from typed/untyped javascript variants through to completely different languages like ocaml, clojurescript, haskell etc.
Well, in my case, I am mainly talking about front-end code. JS is the only viable choice. IMO, making such an effort for the back-end only would be a waste of time, as you said, because of all of the choices. My point is that 50% of a real app runs on the front-end, and JS is the only real choice. In an effort to minimize the number of technologies, the world is increasingly using JS on the back-end too. Gambit already works well on the back-end. If it worked well on the front-end to, perhaps Gambit could offer an alternative solution to the effort to minimize the number of technologies needed to do both front-end and back-end development.
But as someone who really wants to make use of the Gambit javascript backend on an upcoming commercial project I'd certainly appreciate hearing of any issues you have encountered so far.
I am happy to work closely with someone if that is a possibility. Is it?
Thanks.
Blake McBride
Cheers,
James
asmjs is cool and Gambit has already been compiled to it thanks to emscripten (see “Gambit in the browser”: http://feeley.github.io/gambit-in-the-browser/). The performance is not bad, perhaps 10x to 20x slower than when Gambit is compiled by gcc to native code. The main problem is the size of the JS that is generated when compiling Gambit-C with emscripten to JS. The gsi interpreter yields roughly 10 MB of asmjs code.
As far as code size is concerned, a better solution is to use Gambit's JS backend with the Gambit library. The code generated is roughly half the size when minimized (and it could be even less if some thought went into how identifiers are encoded).
Even more savings can be had by doing away with the default Gambit library and writing a custom library specialized for the application. Gambit’s library has lots of functionality that is not normally needed by typical applications. For example, the predefined map procedure can handle an arbitrary number of list parameters. If the application only uses the single list version, then this definition would be sufficient:
(define (map f lst) (if (pair? lst) (cons (f (car lst)) (map f (cdr lst))) ‘()))
That 4 line version is 1/20th the size of the map defined in the Gambit library (which handles multiple lists, has type checking, precise error messages, same list length checking and is safe for multithreading).
So perhaps what’s needed for Gambit to be more successful for web dev is the creation of such a “slim” library for Gambit to replace the default feature-full library. Gambit’s “tree shaker” would also help to eliminate the unused procedures of the slim library (unfortunately this only works for “whole program” compilation, so separate compilation would only be used for the development phase).
Anyway, if this interests you please let me know.
Marc
On Jan 1, 2016, at 11:44 AM, Blake McBride blake@mcbride.name wrote:
Just some opinions.
asmjs.org defines a portable subset that allows JS platforms to compile into very fast code. Targeting that subset, and within their spec, is probably a good idea.
JS has, and is becoming increasingly, a very, very important platform. With ajax and rest services, code increasingly independent from the back-end is being developed. So, in a very important sense, JS has become its own platform, just like X86, and X86_64, along with Linux, Windows, Mac, etc.
Many apps consist of two major parts: the back-end processing, and the front-end human interface. While one can write the back-end processing in any of dozens of languages targeting X86 or a VM, and the OS, there is really only one target for the other half - the human interface - and that is JS.
While many languages are now targeting the JS platform (I am using the word "platform" to mean X86, X86_64, either, plus the OS) including, believe it or not, Smalltalk, there are few that can, IMO, be used in a real world application for several reasons. But, these issues will likely be resolved soon.
With respect, Gambit, at least in the past, was in many ways another toy solution - one with a functioning "Hello world" application but missing important features that make it usable in a real-world situation. I would love to be a part of that solution, but alas, life doesn't offer me that level of freedom. I am only capable of using a system that is reportedly working, reporting bugs, making small adjustments, and providing feedback. Regardless of my attitude, preferences, and intentions, I cannot provide more.
Look at node (JS for the back-end) and its vastly increasing popularity. Since developers are being forced to use JS on the front-end, it's bleeding over to the back-end. I think solutions that take JS seriously at this stage will prosper greatly.
Now, having said all that, I would love to see Gambit target JS as a high-priority, first-class target. If that were the case, I would be happy to contribute what I stated above.
Thanks!
Blake McBride
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
Hi Marc,
Thanks for the reply.
I started development of a Web based business application in 2006. The app is in production use. The front-end is about 450 screens written in Flash. The back-end is written in Java (over 9,000 classes) on top of an SQL database with 250 tables accessed through Hibernate. The front-end and back-end communicate through SOAP web services.
Since Flash is now dead, I need to re-write at least parts of the front-end in JS to support tablets and phones at least. I have front-end JS code that communicates with the back-end SOAP Web services so I theoretically wouldn't need to touch the back-end.
I have a preference to utilize scheme if I can, but the back-end is already written and works. It doesn't make sense to mess with it given the investment.
The problems I had with Gambit (as well as many other systems) is as follows:
Once a user logs in, my app has a consistent visual / UI framework. Beyond this framework, there are 450 different screens that appear within the framework (like in one of the div's).
A. I cannot load all 450 screen at system startup. I must lazy load them as they are called up.
B. The initial UI framework code should contain all of the Gambit machinery and libraries so that each of the 450 screens can be small, light-weight, compiled separately yet have full access to the entire Gambit facility.
With respect, my investigation into Gambit as a possible solution in the past led me to the conclusions at that time it was not possible to produce separately compiled modules that leveraged off of a single main module for the Gambit machinery and libraries.
I suppose some time has passed and I wanted to see if the situation has changed. I can only use Gambit if:
1. I can compile and load each screen separately
2. I only have to have one copy of the Gambit machinery and library so that the separate screens are small.
Thank you.
Blake
On Sat, Jan 2, 2016 at 7:06 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
asmjs is cool and Gambit has already been compiled to it thanks to emscripten (see “Gambit in the browser”: http://feeley.github.io/gambit-in-the-browser/). The performance is not bad, perhaps 10x to 20x slower than when Gambit is compiled by gcc to native code. The main problem is the size of the JS that is generated when compiling Gambit-C with emscripten to JS. The gsi interpreter yields roughly 10 MB of asmjs code.
As far as code size is concerned, a better solution is to use Gambit's JS backend with the Gambit library. The code generated is roughly half the size when minimized (and it could be even less if some thought went into how identifiers are encoded).
Even more savings can be had by doing away with the default Gambit library and writing a custom library specialized for the application. Gambit’s library has lots of functionality that is not normally needed by typical applications. For example, the predefined map procedure can handle an arbitrary number of list parameters. If the application only uses the single list version, then this definition would be sufficient:
(define (map f lst) (if (pair? lst) (cons (f (car lst)) (map f (cdr lst))) ‘()))
That 4 line version is 1/20th the size of the map defined in the Gambit library (which handles multiple lists, has type checking, precise error messages, same list length checking and is safe for multithreading).
So perhaps what’s needed for Gambit to be more successful for web dev is the creation of such a “slim” library for Gambit to replace the default feature-full library. Gambit’s “tree shaker” would also help to eliminate the unused procedures of the slim library (unfortunately this only works for “whole program” compilation, so separate compilation would only be used for the development phase).
Anyway, if this interests you please let me know.
Marc
On Jan 1, 2016, at 11:44 AM, Blake McBride blake@mcbride.name wrote:
Just some opinions.
asmjs.org defines a portable subset that allows JS platforms to compile
into very fast code. Targeting that subset, and within their spec, is probably a good idea.
JS has, and is becoming increasingly, a very, very important platform.
With ajax and rest services, code increasingly independent from the back-end is being developed. So, in a very important sense, JS has become its own platform, just like X86, and X86_64, along with Linux, Windows, Mac, etc.
Many apps consist of two major parts: the back-end processing, and the
front-end human interface. While one can write the back-end processing in any of dozens of languages targeting X86 or a VM, and the OS, there is really only one target for the other half - the human interface - and that is JS.
While many languages are now targeting the JS platform (I am using the
word "platform" to mean X86, X86_64, either, plus the OS) including, believe it or not, Smalltalk, there are few that can, IMO, be used in a real world application for several reasons. But, these issues will likely be resolved soon.
With respect, Gambit, at least in the past, was in many ways another toy
solution - one with a functioning "Hello world" application but missing important features that make it usable in a real-world situation. I would love to be a part of that solution, but alas, life doesn't offer me that level of freedom. I am only capable of using a system that is reportedly working, reporting bugs, making small adjustments, and providing feedback. Regardless of my attitude, preferences, and intentions, I cannot provide more.
Look at node (JS for the back-end) and its vastly increasing
popularity. Since developers are being forced to use JS on the front-end, it's bleeding over to the back-end. I think solutions that take JS seriously at this stage will prosper greatly.
Now, having said all that, I would love to see Gambit target JS as a
high-priority, first-class target. If that were the case, I would be happy to contribute what I stated above.
Thanks!
Blake McBride
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
That’s an interesting app!
As I said, it shouldn’t be hard to do. In your app, how do you load new JS code? Are you using jQuery and the getScript method?
Marc
On Jan 2, 2016, at 8:42 PM, Blake McBride blake@mcbride.name wrote:
Hi Marc,
Thanks for the reply.
I started development of a Web based business application in 2006. The app is in production use. The front-end is about 450 screens written in Flash. The back-end is written in Java (over 9,000 classes) on top of an SQL database with 250 tables accessed through Hibernate. The front-end and back-end communicate through SOAP web services.
Since Flash is now dead, I need to re-write at least parts of the front-end in JS to support tablets and phones at least. I have front-end JS code that communicates with the back-end SOAP Web services so I theoretically wouldn't need to touch the back-end.
I have a preference to utilize scheme if I can, but the back-end is already written and works. It doesn't make sense to mess with it given the investment.
The problems I had with Gambit (as well as many other systems) is as follows:
Once a user logs in, my app has a consistent visual / UI framework. Beyond this framework, there are 450 different screens that appear within the framework (like in one of the div's).
A. I cannot load all 450 screen at system startup. I must lazy load them as they are called up.
B. The initial UI framework code should contain all of the Gambit machinery and libraries so that each of the 450 screens can be small, light-weight, compiled separately yet have full access to the entire Gambit facility.
With respect, my investigation into Gambit as a possible solution in the past led me to the conclusions at that time it was not possible to produce separately compiled modules that leveraged off of a single main module for the Gambit machinery and libraries.
I suppose some time has passed and I wanted to see if the situation has changed. I can only use Gambit if:
I can compile and load each screen separately
I only have to have one copy of the Gambit machinery and library so that the separate screens are small.
Thank you.
Blake
On Sat, Jan 2, 2016 at 7:06 PM, Marc Feeley feeley@iro.umontreal.ca wrote: asmjs is cool and Gambit has already been compiled to it thanks to emscripten (see “Gambit in the browser”: http://feeley.github.io/gambit-in-the-browser/). The performance is not bad, perhaps 10x to 20x slower than when Gambit is compiled by gcc to native code. The main problem is the size of the JS that is generated when compiling Gambit-C with emscripten to JS. The gsi interpreter yields roughly 10 MB of asmjs code.
As far as code size is concerned, a better solution is to use Gambit's JS backend with the Gambit library. The code generated is roughly half the size when minimized (and it could be even less if some thought went into how identifiers are encoded).
Even more savings can be had by doing away with the default Gambit library and writing a custom library specialized for the application. Gambit’s library has lots of functionality that is not normally needed by typical applications. For example, the predefined map procedure can handle an arbitrary number of list parameters. If the application only uses the single list version, then this definition would be sufficient:
(define (map f lst) (if (pair? lst) (cons (f (car lst)) (map f (cdr lst))) ‘()))
That 4 line version is 1/20th the size of the map defined in the Gambit library (which handles multiple lists, has type checking, precise error messages, same list length checking and is safe for multithreading).
So perhaps what’s needed for Gambit to be more successful for web dev is the creation of such a “slim” library for Gambit to replace the default feature-full library. Gambit’s “tree shaker” would also help to eliminate the unused procedures of the slim library (unfortunately this only works for “whole program” compilation, so separate compilation would only be used for the development phase).
Anyway, if this interests you please let me know.
Marc
On Jan 1, 2016, at 11:44 AM, Blake McBride blake@mcbride.name wrote:
Just some opinions.
asmjs.org defines a portable subset that allows JS platforms to compile into very fast code. Targeting that subset, and within their spec, is probably a good idea.
JS has, and is becoming increasingly, a very, very important platform. With ajax and rest services, code increasingly independent from the back-end is being developed. So, in a very important sense, JS has become its own platform, just like X86, and X86_64, along with Linux, Windows, Mac, etc.
Many apps consist of two major parts: the back-end processing, and the front-end human interface. While one can write the back-end processing in any of dozens of languages targeting X86 or a VM, and the OS, there is really only one target for the other half - the human interface - and that is JS.
While many languages are now targeting the JS platform (I am using the word "platform" to mean X86, X86_64, either, plus the OS) including, believe it or not, Smalltalk, there are few that can, IMO, be used in a real world application for several reasons. But, these issues will likely be resolved soon.
With respect, Gambit, at least in the past, was in many ways another toy solution - one with a functioning "Hello world" application but missing important features that make it usable in a real-world situation. I would love to be a part of that solution, but alas, life doesn't offer me that level of freedom. I am only capable of using a system that is reportedly working, reporting bugs, making small adjustments, and providing feedback. Regardless of my attitude, preferences, and intentions, I cannot provide more.
Look at node (JS for the back-end) and its vastly increasing popularity. Since developers are being forced to use JS on the front-end, it's bleeding over to the back-end. I think solutions that take JS seriously at this stage will prosper greatly.
Now, having said all that, I would love to see Gambit target JS as a high-priority, first-class target. If that were the case, I would be happy to contribute what I stated above.
Thanks!
Blake McBride
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
Yes, and I use jQuery get and html methods to load the html part.
On Sat, Jan 2, 2016 at 8:43 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
That’s an interesting app!
As I said, it shouldn’t be hard to do. In your app, how do you load new JS code? Are you using jQuery and the getScript method?
Marc
On Jan 2, 2016, at 8:42 PM, Blake McBride blake@mcbride.name wrote:
Hi Marc,
Thanks for the reply.
I started development of a Web based business application in 2006. The
app is in production use. The front-end is about 450 screens written in Flash. The back-end is written in Java (over 9,000 classes) on top of an SQL database with 250 tables accessed through Hibernate. The front-end and back-end communicate through SOAP web services.
Since Flash is now dead, I need to re-write at least parts of the
front-end in JS to support tablets and phones at least. I have front-end JS code that communicates with the back-end SOAP Web services so I theoretically wouldn't need to touch the back-end.
I have a preference to utilize scheme if I can, but the back-end is
already written and works. It doesn't make sense to mess with it given the investment.
The problems I had with Gambit (as well as many other systems) is as
follows:
Once a user logs in, my app has a consistent visual / UI framework.
Beyond this framework, there are 450 different screens that appear within the framework (like in one of the div's).
A. I cannot load all 450 screen at system startup. I must lazy load
them as they are called up.
B. The initial UI framework code should contain all of the Gambit
machinery and libraries so that each of the 450 screens can be small, light-weight, compiled separately yet have full access to the entire Gambit facility.
With respect, my investigation into Gambit as a possible solution in the
past led me to the conclusions at that time it was not possible to produce separately compiled modules that leveraged off of a single main module for the Gambit machinery and libraries.
I suppose some time has passed and I wanted to see if the situation has
changed. I can only use Gambit if:
I can compile and load each screen separately
I only have to have one copy of the Gambit machinery and library so
that the separate screens are small.
Thank you.
Blake
On Sat, Jan 2, 2016 at 7:06 PM, Marc Feeley feeley@iro.umontreal.ca
wrote:
asmjs is cool and Gambit has already been compiled to it thanks to
emscripten (see “Gambit in the browser”: http://feeley.github.io/gambit-in-the-browser/). The performance is not bad, perhaps 10x to 20x slower than when Gambit is compiled by gcc to native code. The main problem is the size of the JS that is generated when compiling Gambit-C with emscripten to JS. The gsi interpreter yields roughly 10 MB of asmjs code.
As far as code size is concerned, a better solution is to use Gambit's
JS backend with the Gambit library. The code generated is roughly half the size when minimized (and it could be even less if some thought went into how identifiers are encoded).
Even more savings can be had by doing away with the default Gambit
library and writing a custom library specialized for the application. Gambit’s library has lots of functionality that is not normally needed by typical applications. For example, the predefined map procedure can handle an arbitrary number of list parameters. If the application only uses the single list version, then this definition would be sufficient:
(define (map f lst) (if (pair? lst) (cons (f (car lst)) (map f (cdr lst))) ‘()))
That 4 line version is 1/20th the size of the map defined in the Gambit
library (which handles multiple lists, has type checking, precise error messages, same list length checking and is safe for multithreading).
So perhaps what’s needed for Gambit to be more successful for web dev is
the creation of such a “slim” library for Gambit to replace the default feature-full library. Gambit’s “tree shaker” would also help to eliminate the unused procedures of the slim library (unfortunately this only works for “whole program” compilation, so separate compilation would only be used for the development phase).
Anyway, if this interests you please let me know.
Marc
On Jan 1, 2016, at 11:44 AM, Blake McBride blake@mcbride.name wrote:
Just some opinions.
asmjs.org defines a portable subset that allows JS platforms to
compile into very fast code. Targeting that subset, and within their spec, is probably a good idea.
JS has, and is becoming increasingly, a very, very important
platform. With ajax and rest services, code increasingly independent from the back-end is being developed. So, in a very important sense, JS has become its own platform, just like X86, and X86_64, along with Linux, Windows, Mac, etc.
Many apps consist of two major parts: the back-end processing, and
the front-end human interface. While one can write the back-end processing in any of dozens of languages targeting X86 or a VM, and the OS, there is really only one target for the other half - the human interface - and that is JS.
While many languages are now targeting the JS platform (I am using the
word "platform" to mean X86, X86_64, either, plus the OS) including, believe it or not, Smalltalk, there are few that can, IMO, be used in a real world application for several reasons. But, these issues will likely be resolved soon.
With respect, Gambit, at least in the past, was in many ways another
toy solution - one with a functioning "Hello world" application but missing important features that make it usable in a real-world situation. I would love to be a part of that solution, but alas, life doesn't offer me that level of freedom. I am only capable of using a system that is reportedly working, reporting bugs, making small adjustments, and providing feedback. Regardless of my attitude, preferences, and intentions, I cannot provide more.
Look at node (JS for the back-end) and its vastly increasing
popularity. Since developers are being forced to use JS on the front-end, it's bleeding over to the back-end. I think solutions that take JS seriously at this stage will prosper greatly.
Now, having said all that, I would love to see Gambit target JS as a
high-priority, first-class target. If that were the case, I would be happy to contribute what I stated above.
Thanks!
Blake McBride
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
I’ve just pushed a small change to the module registration method. Now, loading the generated .js file will run that file.
Marc
On Jan 2, 2016, at 11:39 PM, Blake McBride blake@mcbride.name wrote:
Yes, and I use jQuery get and html methods to load the html part.
On Sat, Jan 2, 2016 at 8:43 PM, Marc Feeley feeley@iro.umontreal.ca wrote: That’s an interesting app!
As I said, it shouldn’t be hard to do. In your app, how do you load new JS code? Are you using jQuery and the getScript method?
Marc
On Jan 2, 2016, at 8:42 PM, Blake McBride blake@mcbride.name wrote:
Hi Marc,
Thanks for the reply.
I started development of a Web based business application in 2006. The app is in production use. The front-end is about 450 screens written in Flash. The back-end is written in Java (over 9,000 classes) on top of an SQL database with 250 tables accessed through Hibernate. The front-end and back-end communicate through SOAP web services.
Since Flash is now dead, I need to re-write at least parts of the front-end in JS to support tablets and phones at least. I have front-end JS code that communicates with the back-end SOAP Web services so I theoretically wouldn't need to touch the back-end.
I have a preference to utilize scheme if I can, but the back-end is already written and works. It doesn't make sense to mess with it given the investment.
The problems I had with Gambit (as well as many other systems) is as follows:
Once a user logs in, my app has a consistent visual / UI framework. Beyond this framework, there are 450 different screens that appear within the framework (like in one of the div's).
A. I cannot load all 450 screen at system startup. I must lazy load them as they are called up.
B. The initial UI framework code should contain all of the Gambit machinery and libraries so that each of the 450 screens can be small, light-weight, compiled separately yet have full access to the entire Gambit facility.
With respect, my investigation into Gambit as a possible solution in the past led me to the conclusions at that time it was not possible to produce separately compiled modules that leveraged off of a single main module for the Gambit machinery and libraries.
I suppose some time has passed and I wanted to see if the situation has changed. I can only use Gambit if:
I can compile and load each screen separately
I only have to have one copy of the Gambit machinery and library so that the separate screens are small.
Thank you.
Blake
On Sat, Jan 2, 2016 at 7:06 PM, Marc Feeley feeley@iro.umontreal.ca wrote: asmjs is cool and Gambit has already been compiled to it thanks to emscripten (see “Gambit in the browser”: http://feeley.github.io/gambit-in-the-browser/). The performance is not bad, perhaps 10x to 20x slower than when Gambit is compiled by gcc to native code. The main problem is the size of the JS that is generated when compiling Gambit-C with emscripten to JS. The gsi interpreter yields roughly 10 MB of asmjs code.
As far as code size is concerned, a better solution is to use Gambit's JS backend with the Gambit library. The code generated is roughly half the size when minimized (and it could be even less if some thought went into how identifiers are encoded).
Even more savings can be had by doing away with the default Gambit library and writing a custom library specialized for the application. Gambit’s library has lots of functionality that is not normally needed by typical applications. For example, the predefined map procedure can handle an arbitrary number of list parameters. If the application only uses the single list version, then this definition would be sufficient:
(define (map f lst) (if (pair? lst) (cons (f (car lst)) (map f (cdr lst))) ‘()))
That 4 line version is 1/20th the size of the map defined in the Gambit library (which handles multiple lists, has type checking, precise error messages, same list length checking and is safe for multithreading).
So perhaps what’s needed for Gambit to be more successful for web dev is the creation of such a “slim” library for Gambit to replace the default feature-full library. Gambit’s “tree shaker” would also help to eliminate the unused procedures of the slim library (unfortunately this only works for “whole program” compilation, so separate compilation would only be used for the development phase).
Anyway, if this interests you please let me know.
Marc
On Jan 1, 2016, at 11:44 AM, Blake McBride blake@mcbride.name wrote:
Just some opinions.
asmjs.org defines a portable subset that allows JS platforms to compile into very fast code. Targeting that subset, and within their spec, is probably a good idea.
JS has, and is becoming increasingly, a very, very important platform. With ajax and rest services, code increasingly independent from the back-end is being developed. So, in a very important sense, JS has become its own platform, just like X86, and X86_64, along with Linux, Windows, Mac, etc.
Many apps consist of two major parts: the back-end processing, and the front-end human interface. While one can write the back-end processing in any of dozens of languages targeting X86 or a VM, and the OS, there is really only one target for the other half - the human interface - and that is JS.
While many languages are now targeting the JS platform (I am using the word "platform" to mean X86, X86_64, either, plus the OS) including, believe it or not, Smalltalk, there are few that can, IMO, be used in a real world application for several reasons. But, these issues will likely be resolved soon.
With respect, Gambit, at least in the past, was in many ways another toy solution - one with a functioning "Hello world" application but missing important features that make it usable in a real-world situation. I would love to be a part of that solution, but alas, life doesn't offer me that level of freedom. I am only capable of using a system that is reportedly working, reporting bugs, making small adjustments, and providing feedback. Regardless of my attitude, preferences, and intentions, I cannot provide more.
Look at node (JS for the back-end) and its vastly increasing popularity. Since developers are being forced to use JS on the front-end, it's bleeding over to the back-end. I think solutions that take JS seriously at this stage will prosper greatly.
Now, having said all that, I would love to see Gambit target JS as a high-priority, first-class target. If that were the case, I would be happy to contribute what I stated above.
Thanks!
Blake McBride
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
I have added a compiler optimization which removes useless definitions in a program. This can be useful to reduce the generated code size of a program by only including the procedures of a custom library that are needed.
Let me make an example to show what would be required to create a custom library for the universal backend, specifically with JavaScript as a target. But the same optimization applies for the other targets.
Let's start with an empty custom library and a trivial program that prints "hello!":
% cat mylib.scm ;; File: "mylib.scm" % cat myprog.scm ;; File: "myprog.scm" (include "mylib.scm") (println "hello!")
These can be compiled to JavaScript and linked, to produce a single "p.js" file of code like this:
% gsc -target js -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js
This can be run with node.js (on OS X) like this:
% node p.js hello! % wc p.js 322 893 7231 p.js
So the JavaScript code for this trivial program is about 7K of code.
Let's extend the program so that it finds the number of solutions to the 8-queens problem. The Scheme code added to myprog.scm is:
(define trace? #f)
(define (nqueens n)
(define (one-to n) (let loop ((i n) (l '())) (if (= i 0) l (loop (- i 1) (cons i l)))))
(define (attempt x y z) (if (null? x) (if (null? y) (begin (if trace? (println z)) 1) 0) (+ (if (ok? (car x) 1 z) (attempt (append (cdr x) y) '() (cons (car x) z)) 0) (attempt (cdr x) (cons (car x) y) z))))
(define (ok? row dist placed) (if (null? placed) #t (and (not (= (car placed) (+ row dist))) (not (= (car placed) (- row dist))) (ok? row (+ dist 1) (cdr placed)))))
(attempt (one-to n) '() '()))
(println (nqueens 8))
Now let's compile that, and use the "-warnings" flag to see which predefined procedures are missing from our library:
% gsc -target js -warnings -link -flat myprog.scm *** WARNING -- "+" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "=" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "car" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "cons" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "append" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "null?" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "cdr" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "-" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "not" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js")
Except for println, all global variables that should be bound to a predefined procedure are undefined.
So we need to implement these procedures in mylib.scm . We need to use an "extended-bindings" declaration, and a "not safe", for the compiler to inline the primitives used in the definition of the predefined procedures:
(declare (extended-bindings) (not safe))
(define (not x) (##not x))
(define (+ x y) (##fx+ x y)) (define (- x y) (##fx- x y)) (define (= x y) (##fx= x y))
(define (cons x y) (##cons x y)) (define (car x) (##car x)) (define (cdr x) (##car x))
(define (null? x) (##null? x))
(define (append x y) (if (##pair? x) (##cons (##car x) (append (##cdr x) y)) y))
Now the compilation and link will complete with no warnings:
% gsc -target js -warnings -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js % node p.js hello! 92 % wc p.js 3136 8253 67284 p.js
So the program compiles to about 67K of JavaScript code.
In general mylib.scm will contain definitions for predefined Scheme procedures that are not required by myprog.scm . For example:
(define (map f lst) (if (##pair? lst) (##cons (f (##car lst)) (map f (##cdr lst))) '()))
(define (length lst) (let loop ((lst lst) (i 0)) (if (##pair? lst) (loop (##cdr lst) (##fx+ i 1)) i)))
(define (list-ref lst i) (let loop ((lst lst) (i i)) (if (##fx= i 0) (##car lst) (loop (##cdr lst) (##fx- i 1)))))
(define (reverse lst) (let loop ((lst lst) (result '())) (if (##pair? lst) (loop (##cdr lst) (##cons (##car lst) result)) result)))
With these definitions in mylib.scm the program compiles to about 75K of JavaScript code:
% gsc -target js -warnings -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js % wc p.js 3538 9318 75872 p.js
With my latest commit, the compiler is now able to remove the procedure definitions that are not needed by the program when the "optimize-dead-definitions" declaration is used. This line must be added to myprog.scm :
(declare (optimize-dead-definitions))
Now the program compiles to about 67K of JavaScript code. By adding the "block" declaration:
(declare (block))
this can be lowered to 52K:
% gsc -target js -warnings -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js % wc p.js 2144 5870 52490 p.js
The procedure definitions for not, +, -, =, cons, car, cdr, and null? are still being compiled to JavaScript, even though the compiler could inline those primitives. This is achieved by adding this line to myprog.scm :
(declare (standard-bindings) (fixnum))
With this declaration the program compiles to about 15K of JavaScript code:
% gsc -target js -warnings -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js % wc p.js 690 1845 14774 p.js
Finally, with the declaration:
(declare (inlining-limit 0))
some code expansion due to inlining can be avoided, resulting in 14K of JavaScript code:
% gsc -target js -warnings -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js % wc p.js 667 1768 14198 p.js
This code size will stay the same regardless of additional procedure definitions in mylib.scm .
So with the new optimization I think much more compact target code can be generated when using a custom library that is not separately compiled.
Marc
On Jan 2, 2016, at 8:06 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
asmjs is cool and Gambit has already been compiled to it thanks to emscripten (see “Gambit in the browser”: http://feeley.github.io/gambit-in-the-browser/). The performance is not bad, perhaps 10x to 20x slower than when Gambit is compiled by gcc to native code. The main problem is the size of the JS that is generated when compiling Gambit-C with emscripten to JS. The gsi interpreter yields roughly 10 MB of asmjs code.
As far as code size is concerned, a better solution is to use Gambit's JS backend with the Gambit library. The code generated is roughly half the size when minimized (and it could be even less if some thought went into how identifiers are encoded).
Even more savings can be had by doing away with the default Gambit library and writing a custom library specialized for the application. Gambit’s library has lots of functionality that is not normally needed by typical applications. For example, the predefined map procedure can handle an arbitrary number of list parameters. If the application only uses the single list version, then this definition would be sufficient:
(define (map f lst) (if (pair? lst) (cons (f (car lst)) (map f (cdr lst))) ‘()))
That 4 line version is 1/20th the size of the map defined in the Gambit library (which handles multiple lists, has type checking, precise error messages, same list length checking and is safe for multithreading).
So perhaps what’s needed for Gambit to be more successful for web dev is the creation of such a “slim” library for Gambit to replace the default feature-full library. Gambit’s “tree shaker” would also help to eliminate the unused procedures of the slim library (unfortunately this only works for “whole program” compilation, so separate compilation would only be used for the development phase).
Anyway, if this interests you please let me know.
Marc
On Jan 1, 2016, at 11:44 AM, Blake McBride blake@mcbride.name wrote:
Just some opinions.
asmjs.org defines a portable subset that allows JS platforms to compile into very fast code. Targeting that subset, and within their spec, is probably a good idea.
JS has, and is becoming increasingly, a very, very important platform. With ajax and rest services, code increasingly independent from the back-end is being developed. So, in a very important sense, JS has become its own platform, just like X86, and X86_64, along with Linux, Windows, Mac, etc.
Many apps consist of two major parts: the back-end processing, and the front-end human interface. While one can write the back-end processing in any of dozens of languages targeting X86 or a VM, and the OS, there is really only one target for the other half - the human interface - and that is JS.
While many languages are now targeting the JS platform (I am using the word "platform" to mean X86, X86_64, either, plus the OS) including, believe it or not, Smalltalk, there are few that can, IMO, be used in a real world application for several reasons. But, these issues will likely be resolved soon.
With respect, Gambit, at least in the past, was in many ways another toy solution - one with a functioning "Hello world" application but missing important features that make it usable in a real-world situation. I would love to be a part of that solution, but alas, life doesn't offer me that level of freedom. I am only capable of using a system that is reportedly working, reporting bugs, making small adjustments, and providing feedback. Regardless of my attitude, preferences, and intentions, I cannot provide more.
Look at node (JS for the back-end) and its vastly increasing popularity. Since developers are being forced to use JS on the front-end, it's bleeding over to the back-end. I think solutions that take JS seriously at this stage will prosper greatly.
Now, having said all that, I would love to see Gambit target JS as a high-priority, first-class target. If that were the case, I would be happy to contribute what I stated above.
Thanks!
Blake McBride
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
On Jan 12, 2016, at 9:41 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
I have added a compiler optimization which removes useless definitions in a program. This can be useful to reduce the generated code size of a program by only including the procedures of a custom library that are needed.
Pretty cool.
Maybe you could do something similar with a deforestation pass; that would speed up a lot of codes using short vectors in dense computations.
Brad
Deforestation would require a control flow analysis, which is quite a bit more complex than the 40 line optimization I added.
Marc
On Jan 12, 2016, at 10:01 PM, Bradley Lucier lucier@math.purdue.edu wrote:
On Jan 12, 2016, at 9:41 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
I have added a compiler optimization which removes useless definitions in a program. This can be useful to reduce the generated code size of a program by only including the procedures of a custom library that are needed.
Pretty cool.
Maybe you could do something similar with a deforestation pass; that would speed up a lot of codes using short vectors in dense computations.
Brad
What is the transformation you have in mind? It seems to me that for the optimization to operate at the basic block level, the construction of the vector and the indexing would have to be very close to each other, rather unlikely in complex code.
I think the transformation should be done on the ast (in _ptree2.scm).
It would need to take the code
(let ((v (vector E0 E1 E2 E3))) … (vector-ref v 1) … (vector-ref v 2) …)
and, after checking that v is not mutated or passed to a function, convert it to
(let* ((elem0 E0) (elem1 E1) (elem2 E2) (elem3 E3) (v (vector elem0 elem1 elem2 elem3))) … elem1 … elem2 …)
then the compiler would eliminate the call to vector (a side-effect free primitive) if it sees that v is not referenced anywhere else (this is already optimized by the compiler).
But does this pattern occur with enough frequency to justify the time required implementing the optimization?
I think a more frequent pattern is
(define (f a b c) … (vector E0 E1 E2 E3))
(let ((v (f 1 2 3))) … (vector-ref v 1) … (vector-ref v 2) …)
and that is harder to handle.
Marc
On Jan 12, 2016, at 10:49 PM, Bradley Lucier lucier@math.purdue.edu wrote:
On 01/12/2016 10:48 PM, Marc Feeley wrote:
Deforestation would require a control flow analysis, which is quite a bit more complex than the 40 line optimization I added.
OK. Is this true if you do it per basic block after inlining?
Brad
On Jan 12, 2016, at 11:11 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
What is the transformation you have in mind? It seems to me that for the optimization to operate at the basic block level, the construction of the vector and the indexing would have to be very close to each other, rather unlikely in complex code.
I think of transforming angle:apply-force in:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; safe versions of some routines
(define (safe-sqrt x) (sqrt (max x 0.0)))
(define (safe-acos x) (acos (max -1.0 (min 1.0 x))))
; Euclidean (three-space) vector constructor, accessors, and modifiers
(define (make-euclidean x y z) (f64vector x y z))
(define (euclidean-x v) (f64vector-ref v 0)) (define (euclidean-y v) (f64vector-ref v 1)) (define (euclidean-z v) (f64vector-ref v 2))
(define (set-euclidean-x! v a) (f64vector-set! v 0 a)) (define (set-euclidean-y! v a) (f64vector-set! v 1 a)) (define (set-euclidean-z! v a) (f64vector-set! v 2 a))
;(define euclidean-zero '#f64(0.0 0.0 0.0))
; structure of atoms, bonds, angles, and torsions ; generally speaking, properties includes (shared) values ; specific to each atom (bond, angle, ...) type
(define (make-atom-properties mass charge) (f64vector mass charge))
(define (atom-properties-charge properties) (f64vector-ref properties 1))
(define (atom-properties-mass properties) (f64vector-ref properties 0))
(define (make-atom position velocity force index properties) (vector position velocity force index properties))
(define (atom-position atom) (vector-ref atom 0))
(define (atom-velocity atom) (vector-ref atom 1))
(define (atom-force atom) (vector-ref atom 2))
(define (atom-index atom) (vector-ref atom 3))
(define (atom-properties atom) (vector-ref atom 4))
(define (atom-mass atom) (atom-properties-mass (atom-properties atom)))
(define (atom-charge atom) (atom-properties-charge (atom-properties atom)))
(define (make-angle atom1 atom2 atom3 ideal-angle force-constant) (vector atom1 atom2 atom3 ideal-angle force-constant))
(define (angle-atom1 angle) (vector-ref angle 0))
(define (angle-atom2 angle) (vector-ref angle 1))
(define (angle-atom3 angle) (vector-ref angle 2))
(define (angle-ideal-angle angle) (vector-ref angle 3))
(define (angle-force-constant angle) (vector-ref angle 4))
(define (make-bond atom1 atom2 ideal-length force-constant) (vector atom1 atom2 ideal-length force-constant))
(define (bond-atom1 bond) (vector-ref bond 0))
(define (bond-atom2 bond) (vector-ref bond 1))
(define (bond-ideal-length bond) (vector-ref bond 2))
(define (bond-force-constant bond) (vector-ref bond 3))
; Euclidean vector operations
(define (euclidean:copy! v1 v2) (set-euclidean-x! v1 (euclidean-x v2)) (set-euclidean-y! v1 (euclidean-y v2)) (set-euclidean-z! v1 (euclidean-z v2)))
;(define (euclidean:= v1 v2) ; (and (= (euclidean-x v1) (euclidean-x v2)) ; (= (euclidean-y v1) (euclidean-y v2)) ; (= (euclidean-z v1) (euclidean-z v2))))
;(define (euclidean:zero? v) ; (euclidean:= v euclidean-zero))
;(define (euclidean:zero! v) ; (euclidean:copy! v euclidean-zero))
(define (euclidean:add v1 v2) (make-euclidean (+ (euclidean-x v1) (euclidean-x v2)) (+ (euclidean-y v1) (euclidean-y v2)) (+ (euclidean-z v1) (euclidean-z v2))))
(define (euclidean:subtract v1 v2) (make-euclidean (- (euclidean-x v1) (euclidean-x v2)) (- (euclidean-y v1) (euclidean-y v2)) (- (euclidean-z v1) (euclidean-z v2))))
(define (euclidean:dot v1 v2) (+ (* (euclidean-x v1) (euclidean-x v2)) (* (euclidean-y v1) (euclidean-y v2)) (* (euclidean-z v1) (euclidean-z v2))))
(define (euclidean:cross v1 v2) (make-euclidean (- (* (euclidean-y v1) (euclidean-z v2)) (* (euclidean-z v1) (euclidean-y v2))) (- (* (euclidean-z v1) (euclidean-x v2)) (* (euclidean-x v1) (euclidean-z v2))) (- (* (euclidean-x v1) (euclidean-y v2)) (* (euclidean-y v1) (euclidean-x v2)))))
(define (euclidean:length v) (sqrt (euclidean:dot v v)))
(define (euclidean:scale a v) (make-euclidean (* a (euclidean-x v)) (* a (euclidean-y v)) (* a (euclidean-z v))))
(define (euclidean:perpendicular-component v1 v2) (euclidean:subtract v1 (euclidean:scale (/ (euclidean:dot v1 v2) (euclidean:dot v2 v2)) v2)))
(define (euclidean:angle v1 v2) (safe-acos (/ (euclidean:dot v1 v2) (* (euclidean:length v1) (euclidean:length v2)))))
(define (angle:derivative-of-energy angle theta) (let ((tdif (- theta (angle-ideal-angle angle)))) (* (angle-force-constant angle) (* tdif (+ 1.0 (* 1.508 tdif tdif))))))
(define (angle:apply-force angle) (let* ((atom1 (angle-atom1 angle)) (atom2 (angle-atom2 angle)) (atom3 (angle-atom3 angle)) (r1r2 (euclidean:subtract (atom-position atom1) (atom-position atom2))) (r3r2 (euclidean:subtract (atom-position atom3) (atom-position atom2))) (length-r1r2^2 (euclidean:dot r1r2 r1r2)) (length-r3r2^2 (euclidean:dot r3r2 r3r2)) (length-r1r2 (sqrt length-r1r2^2)) (length-r3r2 (sqrt length-r3r2^2)) (cos-theta (/ (euclidean:dot r1r2 r3r2) (* length-r1r2 length-r3r2))) (theta (safe-acos cos-theta)) (derivative-of-energy (/ (* 2.0 (angle-force-constant angle) (- theta (angle-ideal-angle angle))) (sin theta))) (F1 (euclidean:subtract (euclidean:scale (/ (* cos-theta derivative-of-energy) length-r1r2^2) r1r2) (euclidean:scale (/ derivative-of-energy (* length-r1r2 length-r3r2)) r3r2))) (F3 (euclidean:subtract (euclidean:scale (/ (* cos-theta derivative-of-energy) length-r3r2^2) r3r2) (euclidean:scale (/ derivative-of-energy (* length-r1r2 length-r3r2)) r1r2)))) (euclidean:copy! (atom-force atom1) (euclidean:add (atom-force atom1) F1)) (euclidean:copy! (atom-force atom2) (euclidean:subtract (euclidean:subtract (atom-force atom2) F1) F3)) (euclidean:copy! (atom-force atom3) (euclidean:add (atom-force atom3) F3))))
(define (bond:compute-length-force bond) (let ((atom1 (bond-atom1 bond)) (atom2 (bond-atom2 bond))) (let ((r1r2 (euclidean:subtract (atom-position atom1) (atom-position atom2)))) (let ((length-of-r1r2 (euclidean:length r1r2))) (let ((derivative-of-energy (* (bond-force-constant bond) (- length-of-r1r2 (bond-ideal-length bond))))) (euclidean:scale (/ derivative-of-energy length-of-r1r2) r1r2))))))
(define (bond:apply-length-force bond) (let ((atom1 (bond-atom1 bond)) (atom2 (bond-atom2 bond)) (force (bond:compute-length-force bond))) (euclidean:copy! (atom-force atom1) (euclidean:subtract (atom-force atom1) force)) (euclidean:copy! (atom-force atom2) (euclidean:add (atom-force atom2) force))))
(define (non-bond:compute-vdw-force atom1 atom2 one-four) (let* ((r1r2 (euclidean:subtract (atom-position atom1) (atom-position atom2))) (length-of-r1r2 (euclidean:length r1r2)) (vdw-radius (if one-four (+ (atom-half-vdw-1-4-radius atom1) (atom-half-vdw-1-4-radius atom2)) (+ (atom-half-vdw-radius atom1) (atom-half-vdw-radius atom2)))) (vdw-const (if one-four (* (atom-sqrt-vdw-1-4-const atom1) (atom-sqrt-vdw-1-4-const atom2)) (* (atom-sqrt-vdw-const atom1) (atom-sqrt-vdw-const atom2)))) (ratio (/ vdw-radius length-of-r1r2)) (ratio^2 (* ratio ratio)) (ratio^6 (* ratio^2 ratio^2 ratio^2)) (derivative-of-energy (* vdw-const ratio ratio^6 (- 1.0 (* 2.0 ratio^6))))) (euclidean:scale (/ derivative-of-energy length-of-r1r2) r1r2)))
(define (non-bond:apply-vdw-force atom1 atom2 one-four) (let ((force (non-bond:compute-vdw-force atom1 atom2 one-four))) (euclidean:copy! (atom-force atom1) (euclidean:subtract (atom-force atom1) force)) (euclidean:copy! (atom-force atom2) (euclidean:add (atom-force atom2) force))))
(define (non-bond:compute-electrostatic-force atom1 atom2 one-four) (let* ((r1r2 (euclidean:subtract (atom-position atom1) (atom-position atom2))) (length-of-r1r2^2 (euclidean:dot r1r2 r1r2)) (length-of-r1r2 (sqrt length-of-r1r2^2)) (epsilon (if one-four non-bond:electrostatic-1-4-scaling 1.0))) (euclidean:scale (/ (* non-bond:electrostatic-constant epsilon (atom-charge atom1) (atom-charge atom2)) (* non-bond:dielectric-constant length-of-r1r2^2 length-of-r1r2)) r1r2)))
(define (non-bond:apply-electrostatic-force atom1 atom2 one-four) (let ((force (non-bond:compute-electrostatic-force atom1 atom2 one-four))) (euclidean:copy! (atom-force atom1) (euclidean:subtract (atom-force atom1) force)) (euclidean:copy! (atom-force atom2) (euclidean:add (atom-force atom2) force))))
(define (non-bond:apply-vdw-and-electrostatic-force atom1 atom2 one-four) (let ((force (euclidean:add (non-bond:compute-electrostatic-force atom1 atom2 one-four) (non-bond:compute-vdw-force atom1 atom2 one-four)))) (euclidean:copy! (atom-force atom1) (euclidean:subtract (atom-force atom1) force)) (euclidean:copy! (atom-force atom2) (euclidean:add (atom-force atom2) force))))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
into
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(define angle:apply-force (lambda (angle) (let* ((g287 (vector-ref angle 0)) (g288 (vector-ref g287 2)) (g289 (f64vector-ref g288 0)) (g290 (vector-ref g287 0)) (g291 (f64vector-ref g290 0)) (g292 (vector-ref angle 1)) (g293 (vector-ref g292 0)) (g294 (f64vector-ref g293 0)) (g295 (- g291 g294)) (g296 (vector-ref angle 2)) (g297 (vector-ref g296 0)) (g298 (f64vector-ref g297 0)) (g299 (- g298 g294)) (g300 (* g295 g299)) (g301 (f64vector-ref g290 1)) (g302 (f64vector-ref g293 1)) (g303 (- g301 g302)) (g304 (f64vector-ref g297 1)) (g305 (- g304 g302)) (g306 (* g303 g305)) (g307 (f64vector-ref g290 2)) (g308 (f64vector-ref g293 2)) (g309 (- g307 g308)) (g310 (f64vector-ref g297 2)) (g311 (- g310 g308)) (g312 (* g309 g311)) (g313 (+ g300 g306 g312)) (g314 (* g295 g295)) (g315 (* g303 g303)) (g316 (* g309 g309)) (g317 (+ g314 g315 g316)) (g318 (sqrt g317)) (g319 (* g299 g299)) (g320 (* g305 g305)) (g321 (* g311 g311)) (g322 (+ g319 g320 g321)) (g323 (sqrt g322)) (g324 (* g318 g323)) (g325 (/ g313 g324)) (g326 (vector-ref angle 4)) (g327 (min 1. g325)) (g328 (max -1. g327)) (g329 (acos g328)) (g330 (vector-ref angle 3)) (g331 (- g329 g330)) (g332 (* 2. g326 g331)) (g333 (sin g329)) (g334 (/ g332 g333)) (g335 (* g325 g334)) (g336 (/ g335 g317)) (g337 (* g336 g295)) (g338 (/ g334 g324)) (g339 (* g338 g299)) (g340 (- g337 g339)) (g341 (+ g289 g340)) (g342 (f64vector-set! g288 0 g341)) (g343 (f64vector-ref g288 1)) (g344 (* g336 g303)) (g345 (* g338 g305)) (g346 (- g344 g345)) (g347 (+ g343 g346)) (g348 (f64vector-set! g288 1 g347)) (g349 (f64vector-ref g288 2)) (g350 (* g336 g309)) (g351 (* g338 g311)) (g352 (- g350 g351)) (g353 (+ g349 g352)) (g354 (f64vector-set! g288 2 g353)) (g355 (vector-ref g292 2)) (g356 (f64vector-ref g355 0)) (g357 (- g356 g340)) (g358 (/ g335 g322)) (g359 (* g358 g299)) (g360 (* g338 g295)) (g361 (- g359 g360)) (g362 (- g357 g361)) (g363 (f64vector-set! g355 0 g362)) (g364 (f64vector-ref g355 1)) (g365 (- g364 g346)) (g366 (* g358 g305)) (g367 (* g338 g303)) (g368 (- g366 g367)) (g369 (- g365 g368)) (g370 (f64vector-set! g355 1 g369)) (g371 (f64vector-ref g355 2)) (g372 (- g371 g352)) (g373 (* g358 g311)) (g374 (* g338 g309)) (g375 (- g373 g374)) (g376 (- g372 g375)) (g377 (f64vector-set! g355 2 g376)) (g378 (vector-ref g296 2)) (g379 (f64vector-ref g378 0)) (g380 (+ g379 g361)) (g381 (f64vector-set! g378 0 g380)) (g382 (f64vector-ref g378 1)) (g383 (+ g382 g368)) (g384 (f64vector-set! g378 1 g383)) (g385 (f64vector-ref g378 2)) (g386 (+ g385 g375))) (f64vector-set! g378 2 g386))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
etc. After inlining all the basic operations are close to one another.
Brad
On Jan 12, 2016, at 11:39 PM, Bradley Lucier lucier@math.purdue.edu wrote:
On Jan 12, 2016, at 11:11 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
What is the transformation you have in mind? It seems to me that for the optimization to operate at the basic block level, the construction of the vector and the indexing would have to be very close to each other, rather unlikely in complex code.
I think of transforming angle:apply-force in:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
<omitted>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
into
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(define angle:apply-force (lambda (angle) (let* ((g287 (vector-ref angle 0)) (g288 (vector-ref g287 2)) (g289 (f64vector-ref g288 0)) (g290 (vector-ref g287 0)) (g291 (f64vector-ref g290 0)) (g292 (vector-ref angle 1)) (g293 (vector-ref g292 0)) (g294 (f64vector-ref g293 0)) (g295 (- g291 g294)) (g296 (vector-ref angle 2)) (g297 (vector-ref g296 0)) (g298 (f64vector-ref g297 0)) (g299 (- g298 g294)) (g300 (* g295 g299)) (g301 (f64vector-ref g290 1)) (g302 (f64vector-ref g293 1)) (g303 (- g301 g302)) (g304 (f64vector-ref g297 1)) (g305 (- g304 g302)) (g306 (* g303 g305)) (g307 (f64vector-ref g290 2)) (g308 (f64vector-ref g293 2)) (g309 (- g307 g308)) (g310 (f64vector-ref g297 2)) (g311 (- g310 g308)) (g312 (* g309 g311)) (g313 (+ g300 g306 g312)) (g314 (* g295 g295)) (g315 (* g303 g303)) (g316 (* g309 g309)) (g317 (+ g314 g315 g316)) (g318 (sqrt g317)) (g319 (* g299 g299)) (g320 (* g305 g305)) (g321 (* g311 g311)) (g322 (+ g319 g320 g321)) (g323 (sqrt g322)) (g324 (* g318 g323)) (g325 (/ g313 g324)) (g326 (vector-ref angle 4)) (g327 (min 1. g325)) (g328 (max -1. g327)) (g329 (acos g328)) (g330 (vector-ref angle 3)) (g331 (- g329 g330)) (g332 (* 2. g326 g331)) (g333 (sin g329)) (g334 (/ g332 g333)) (g335 (* g325 g334)) (g336 (/ g335 g317)) (g337 (* g336 g295)) (g338 (/ g334 g324)) (g339 (* g338 g299)) (g340 (- g337 g339)) (g341 (+ g289 g340)) (g342 (f64vector-set! g288 0 g341)) (g343 (f64vector-ref g288 1)) (g344 (* g336 g303)) (g345 (* g338 g305)) (g346 (- g344 g345)) (g347 (+ g343 g346)) (g348 (f64vector-set! g288 1 g347)) (g349 (f64vector-ref g288 2)) (g350 (* g336 g309)) (g351 (* g338 g311)) (g352 (- g350 g351)) (g353 (+ g349 g352)) (g354 (f64vector-set! g288 2 g353)) (g355 (vector-ref g292 2)) (g356 (f64vector-ref g355 0)) (g357 (- g356 g340)) (g358 (/ g335 g322)) (g359 (* g358 g299)) (g360 (* g338 g295)) (g361 (- g359 g360)) (g362 (- g357 g361)) (g363 (f64vector-set! g355 0 g362)) (g364 (f64vector-ref g355 1)) (g365 (- g364 g346)) (g366 (* g358 g305)) (g367 (* g338 g303)) (g368 (- g366 g367)) (g369 (- g365 g368)) (g370 (f64vector-set! g355 1 g369)) (g371 (f64vector-ref g355 2)) (g372 (- g371 g352)) (g373 (* g358 g311)) (g374 (* g338 g309)) (g375 (- g373 g374)) (g376 (- g372 g375)) (g377 (f64vector-set! g355 2 g376)) (g378 (vector-ref g296 2)) (g379 (f64vector-ref g378 0)) (g380 (+ g379 g361)) (g381 (f64vector-set! g378 0 g380)) (g382 (f64vector-ref g378 1)) (g383 (+ g382 g368)) (g384 (f64vector-set! g378 1 g383)) (g385 (f64vector-ref g378 2)) (g386 (+ g385 g375))) (f64vector-set! g378 2 g386))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
etc. After inlining all the basic operations are close to one another.
Just for the record, here’s what you get from current gsc with the declarations
(declare (standard-bindings) (extended-bindings) (block) (flonum) (not safe) (inlining-limit 10000))
including 12 unnecessary calls to ##f64vector, for a total allocation of 384 bytes (which probably takes longer than all the floating-point operations combined), plus untold numbers of unnecessary f64vector-refs to get arguments back out of those f64vectors.
And it’s all just one basic block.
Brad
(define angle:apply-force (lambda (angle) (let ((atom1 ('#<procedure #10 ##vector-ref> angle 0))) (let ((atom2 ('#<procedure #10 ##vector-ref> angle 1))) (let ((atom3 ('#<procedure #10 ##vector-ref> angle 2))) (let ((r1r2 (let ((v2 ('#<procedure #10 ##vector-ref> atom2 0)) (v1 ('#<procedure #10 ##vector-ref> atom1 0))) (let ((x ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 0) ('#<procedure #7 ##f64vector-ref> v2 0))) (y ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 1) ('#<procedure #7 ##f64vector-ref> v2 1))) (z ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 2) ('#<procedure #7 ##f64vector-ref> v2 2)))) ('#<procedure #6 ##f64vector> x y z))))) (let ((r3r2 (let ((v2 ('#<procedure #10 ##vector-ref> atom2 0)) (v1 ('#<procedure #10 ##vector-ref> atom3 0))) (let ((x ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 0) ('#<procedure #7 ##f64vector-ref> v2 0))) (y ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 1) ('#<procedure #7 ##f64vector-ref> v2 1))) (z ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 2) ('#<procedure #7 ##f64vector-ref> v2 2)))) ('#<procedure #6 ##f64vector> x y z))))) (let ((length-r1r2^2 ('#<procedure #11 ##fl+> ('#<procedure #13 ##fl*> ('#<procedure #7 ##f64vector-ref> r1r2 0) ('#<procedure #7 ##f64vector-ref> r1r2 0)) ('#<procedure #13 ##fl*> ('#<procedure #7 ##f64vector-ref> r1r2 1) ('#<procedure #7 ##f64vector-ref> r1r2 1)) ('#<procedure #13 ##fl*> ('#<procedure #7 ##f64vector-ref> r1r2 2) ('#<procedure #7 ##f64vector-ref> r1r2 2))))) (let ((length-r3r2^2 ('#<procedure #11 ##fl+> ('#<procedure #13 ##fl*> ('#<procedure #7 ##f64vector-ref> r3r2 0) ('#<procedure #7 ##f64vector-ref> r3r2 0)) ('#<procedure #13 ##fl*> ('#<procedure #7 ##f64vector-ref> r3r2 1) ('#<procedure #7 ##f64vector-ref> r3r2 1)) ('#<procedure #13 ##fl*> ('#<procedure #7 ##f64vector-ref> r3r2 2) ('#<procedure #7 ##f64vector-ref> r3r2 2))))) (let ((length-r1r2 ('#<procedure #2 ##flsqrt> length-r1r2^2))) (let ((length-r3r2 ('#<procedure #2 ##flsqrt> length-r3r2^2))) (let ((cos-theta ('#<procedure #14 ##fl/> ('#<procedure #11 ##fl+> ('#<procedure #13 ##fl*> ('#<procedure #7 ##f64vector-ref> r1r2 0) ('#<procedure #7 ##f64vector-ref> r3r2 0)) ('#<procedure #13 ##fl*> ('#<procedure #7 ##f64vector-ref> r1r2 1) ('#<procedure #7 ##f64vector-ref> r3r2 1)) ('#<procedure #13 ##fl*> ('#<procedure #7 ##f64vector-ref> r1r2 2) ('#<procedure #7 ##f64vector-ref> r3r2 2))) ('#<procedure #13 ##fl*> length-r1r2 length-r3r2)))) (let ((theta ('#<procedure #4 ##flacos> ('#<procedure #3 ##flmax> -1. ('#<procedure #5 ##flmin> 1. cos-theta))))) (let ((derivative-of-energy ('#<procedure #14 ##fl/> ('#<procedure #13 ##fl*> 2. ('#<procedure #10 ##vector-ref> angle 4) ('#<procedure #12 ##fl-> theta ('#<procedure #10 ##vector-ref> angle 3))) ('#<procedure #15 ##flsin> theta)))) (let ((F1 (let ((v2 (let ((a ('#<procedure #14 ##fl/> derivative-of-energy ('#<procedure #13 ##fl*> length-r1r2 length-r3r2)))) (let ((z ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r3r2 2))) (y ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r3r2 1))) (x ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r3r2 0)))) ('#<procedure #6 ##f64vector> x y z)))) (v1 (let ((a ('#<procedure #14 ##fl/> ('#<procedure #13 ##fl*> cos-theta derivative-of-energy) length-r1r2^2))) (let ((z ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r1r2 2))) (y ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r1r2 1))) (x ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r1r2 0)))) ('#<procedure #6 ##f64vector> x y z))))) (let ((x ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 0) ('#<procedure #7 ##f64vector-ref> v2 0))) (y ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 1) ('#<procedure #7 ##f64vector-ref> v2 1))) (z ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 2) ('#<procedure #7 ##f64vector-ref> v2 2)))) ('#<procedure #6 ##f64vector> x y z))))) (let ((F3 (let ((v2 (let ((a ('#<procedure #14 ##fl/> derivative-of-energy ('#<procedure #13 ##fl*> length-r1r2 length-r3r2)))) (let ((z ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r1r2 2))) (y ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r1r2 1))) (x ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r1r2 0)))) ('#<procedure #6 ##f64vector> x y z)))) (v1 (let ((a ('#<procedure #14 ##fl/> ('#<procedure #13 ##fl*> cos-theta derivative-of-energy) length-r3r2^2))) (let ((z ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r3r2 2))) (y ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r3r2 1))) (x ('#<procedure #13 ##fl*> a ('#<procedure #7 ##f64vector-ref> r3r2 0)))) ('#<procedure #6 ##f64vector> x y z))))) (let ((x ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 0) ('#<procedure #7 ##f64vector-ref> v2 0))) (y ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 1) ('#<procedure #7 ##f64vector-ref> v2 1))) (z ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 2) ('#<procedure #7 ##f64vector-ref> v2 2)))) ('#<procedure #6 ##f64vector> x y z))))) (let ((begin-temp.3 (let ((v2 (let ((v1 ('#<procedure #10 ##vector-ref> atom1 2))) (let ((z ('#<procedure #11 ##fl+> ('#<procedure #7 ##f64vector-ref> v1 2) ('#<procedure #7 ##f64vector-ref> F1 2))) (y ('#<procedure #11 ##fl+> ('#<procedure #7 ##f64vector-ref> v1 1) ('#<procedure #7 ##f64vector-ref> F1 1))) (x ('#<procedure #11 ##fl+> ('#<procedure #7 ##f64vector-ref> v1 0) ('#<procedure #7 ##f64vector-ref> F1 0)))) ('#<procedure #6 ##f64vector> x y z)))) (v1 ('#<procedure #10 ##vector-ref> atom1 2))) (let ((begin-temp.1 (let ((a ('#<procedure #7 ##f64vector-ref> v2 0))) ('#<procedure #8 ##f64vector-set!> v1 0 a)))) (let ((begin-temp.0 (let ((a ('#<procedure #7 ##f64vector-ref> v2 1))) ('#<procedure #8 ##f64vector-set!> v1 1 a)))) (let ((a ('#<procedure #7 ##f64vector-ref> v2 2))) ('#<procedure #8 ##f64vector-set!> v1 2 a))))))) (let ((begin-temp.2 (let ((v2 (let ((v1 (let ((v1 ('#<procedure #10 ##vector-ref> ;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< atom2 2))) (let ((x ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 0) ('#<procedure #7 ##f64vector-ref> F1 0))) (y ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 1) ('#<procedure #7 ##f64vector-ref> F1 1))) (z ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 2) ('#<procedure #7 ##f64vector-ref> F1 2)))) ('#<procedure #6 ##f64vector> x y z))))) ;;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> (let ((z ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 2) ('#<procedure #7 ##f64vector-ref> F3 2))) (y ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 1) ('#<procedure #7 ##f64vector-ref> F3 1))) (x ('#<procedure #12 ##fl-> ('#<procedure #7 ##f64vector-ref> v1 0) ('#<procedure #7 ##f64vector-ref> F3 0)))) ('#<procedure #6 ##f64vector> x y z)))) (v1 ('#<procedure #10 ##vector-ref> atom2 2))) (let ((begin-temp.1 (let ((a ('#<procedure #7 ##f64vector-ref> v2 0))) ('#<procedure #8 ##f64vector-set!> v1 0 a)))) (let ((begin-temp.0 (let ((a ('#<procedure #7 ##f64vector-ref> v2 1))) ('#<procedure #8 ##f64vector-set!> v1 1 a)))) (let ((a ('#<procedure #7 ##f64vector-ref> v2 2))) ('#<procedure #8 ##f64vector-set!> v1 2 a))))))) (let ((v2 (let ((v1 ('#<procedure #10 ##vector-ref> atom3 2))) (let ((z ('#<procedure #11 ##fl+> ('#<procedure #7 ##f64vector-ref> v1 2) ('#<procedure #7 ##f64vector-ref> F3 2))) (y ('#<procedure #11 ##fl+> ('#<procedure #7 ##f64vector-ref> v1 1) ('#<procedure #7 ##f64vector-ref> F3 1))) (x ('#<procedure #11 ##fl+> ('#<procedure #7 ##f64vector-ref> v1 0) ('#<procedure #7 ##f64vector-ref> F3 0)))) ('#<procedure #6 ##f64vector> x y z)))) (v1 ('#<procedure #10 ##vector-ref> atom3 2))) (let ((begin-temp.1 (let ((a ('#<procedure #7 ##f64vector-ref> v2 0))) ('#<procedure #8 ##f64vector-set!> v1 0 a)))) (let ((begin-temp.0 (let ((a ('#<procedure #7 ##f64vector-ref> v2 1))) ('#<procedure #8 ##f64vector-set!> v1 1 a)))) (let ((a ('#<procedure #7 ##f64vector-ref> v2 2))) ('#<procedure #8 ##f64vector-set!> v1 2 a)))))))))))))))))))))))
I also tried using the standard Gambit library by replacing
(include "mylib.scm")
with
(include "~~lib/header.scm") (include "~~lib/_univlib.scm")
This generates a 4.3M file with the optimize-dead-definitions rather than a 7M file without, so a 40% savings. I think the standard Gambit library could be better structured to have fewer dependencies so as to yield more dead definitions. It would be nice also to have a way to disable features that cause bloat, such as the I/O, eval and the REPL (these are probably not “dead” in my example because the primordial exception handler is written so that it pops up a REPL when there is an error, and the REPL requires eval, read, write, etc).
Marc
On Jan 12, 2016, at 9:41 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
I have added a compiler optimization which removes useless definitions in a program. This can be useful to reduce the generated code size of a program by only including the procedures of a custom library that are needed.
Let me make an example to show what would be required to create a custom library for the universal backend, specifically with JavaScript as a target. But the same optimization applies for the other targets.
Let's start with an empty custom library and a trivial program that prints "hello!":
% cat mylib.scm ;; File: "mylib.scm" % cat myprog.scm ;; File: "myprog.scm" (include "mylib.scm") (println "hello!")
These can be compiled to JavaScript and linked, to produce a single "p.js" file of code like this:
% gsc -target js -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js
This can be run with node.js (on OS X) like this:
% node p.js hello! % wc p.js 322 893 7231 p.js
So the JavaScript code for this trivial program is about 7K of code.
Let's extend the program so that it finds the number of solutions to the 8-queens problem. The Scheme code added to myprog.scm is:
(define trace? #f)
(define (nqueens n)
(define (one-to n) (let loop ((i n) (l '())) (if (= i 0) l (loop (- i 1) (cons i l)))))
(define (attempt x y z) (if (null? x) (if (null? y) (begin (if trace? (println z)) 1) 0) (+ (if (ok? (car x) 1 z) (attempt (append (cdr x) y) '() (cons (car x) z)) 0) (attempt (cdr x) (cons (car x) y) z))))
(define (ok? row dist placed) (if (null? placed) #t (and (not (= (car placed) (+ row dist))) (not (= (car placed) (- row dist))) (ok? row (+ dist 1) (cdr placed)))))
(attempt (one-to n) '() '()))
(println (nqueens 8))
Now let's compile that, and use the "-warnings" flag to see which predefined procedures are missing from our library:
% gsc -target js -warnings -link -flat myprog.scm *** WARNING -- "+" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "=" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "car" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "cons" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "append" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "null?" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "cdr" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "-" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js") *** WARNING -- "not" is not defined, *** referenced in: ("/Users/feeley/gambit/work/myprog.js")
Except for println, all global variables that should be bound to a predefined procedure are undefined.
So we need to implement these procedures in mylib.scm . We need to use an "extended-bindings" declaration, and a "not safe", for the compiler to inline the primitives used in the definition of the predefined procedures:
(declare (extended-bindings) (not safe))
(define (not x) (##not x))
(define (+ x y) (##fx+ x y)) (define (- x y) (##fx- x y)) (define (= x y) (##fx= x y))
(define (cons x y) (##cons x y)) (define (car x) (##car x)) (define (cdr x) (##car x))
(define (null? x) (##null? x))
(define (append x y) (if (##pair? x) (##cons (##car x) (append (##cdr x) y)) y))
Now the compilation and link will complete with no warnings:
% gsc -target js -warnings -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js % node p.js hello! 92 % wc p.js 3136 8253 67284 p.js
So the program compiles to about 67K of JavaScript code.
In general mylib.scm will contain definitions for predefined Scheme procedures that are not required by myprog.scm . For example:
(define (map f lst) (if (##pair? lst) (##cons (f (##car lst)) (map f (##cdr lst))) '()))
(define (length lst) (let loop ((lst lst) (i 0)) (if (##pair? lst) (loop (##cdr lst) (##fx+ i 1)) i)))
(define (list-ref lst i) (let loop ((lst lst) (i i)) (if (##fx= i 0) (##car lst) (loop (##cdr lst) (##fx- i 1)))))
(define (reverse lst) (let loop ((lst lst) (result '())) (if (##pair? lst) (loop (##cdr lst) (##cons (##car lst) result)) result)))
With these definitions in mylib.scm the program compiles to about 75K of JavaScript code:
% gsc -target js -warnings -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js % wc p.js 3538 9318 75872 p.js
With my latest commit, the compiler is now able to remove the procedure definitions that are not needed by the program when the "optimize-dead-definitions" declaration is used. This line must be added to myprog.scm :
(declare (optimize-dead-definitions))
Now the program compiles to about 67K of JavaScript code. By adding the "block" declaration:
(declare (block))
this can be lowered to 52K:
% gsc -target js -warnings -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js % wc p.js 2144 5870 52490 p.js
The procedure definitions for not, +, -, =, cons, car, cdr, and null? are still being compiled to JavaScript, even though the compiler could inline those primitives. This is achieved by adding this line to myprog.scm :
(declare (standard-bindings) (fixnum))
With this declaration the program compiles to about 15K of JavaScript code:
% gsc -target js -warnings -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js % wc p.js 690 1845 14774 p.js
Finally, with the declaration:
(declare (inlining-limit 0))
some code expansion due to inlining can be avoided, resulting in 14K of JavaScript code:
% gsc -target js -warnings -link -flat myprog.scm ; cat myprog_.js myprog.js > p.js % wc p.js 667 1768 14198 p.js
This code size will stay the same regardless of additional procedure definitions in mylib.scm .
So with the new optimization I think much more compact target code can be generated when using a custom library that is not separately compiled.
Marc
On Jan 2, 2016, at 8:06 PM, Marc Feeley feeley@iro.umontreal.ca wrote:
asmjs is cool and Gambit has already been compiled to it thanks to emscripten (see “Gambit in the browser”: http://feeley.github.io/gambit-in-the-browser/). The performance is not bad, perhaps 10x to 20x slower than when Gambit is compiled by gcc to native code. The main problem is the size of the JS that is generated when compiling Gambit-C with emscripten to JS. The gsi interpreter yields roughly 10 MB of asmjs code.
As far as code size is concerned, a better solution is to use Gambit's JS backend with the Gambit library. The code generated is roughly half the size when minimized (and it could be even less if some thought went into how identifiers are encoded).
Even more savings can be had by doing away with the default Gambit library and writing a custom library specialized for the application. Gambit’s library has lots of functionality that is not normally needed by typical applications. For example, the predefined map procedure can handle an arbitrary number of list parameters. If the application only uses the single list version, then this definition would be sufficient:
(define (map f lst) (if (pair? lst) (cons (f (car lst)) (map f (cdr lst))) ‘()))
That 4 line version is 1/20th the size of the map defined in the Gambit library (which handles multiple lists, has type checking, precise error messages, same list length checking and is safe for multithreading).
So perhaps what’s needed for Gambit to be more successful for web dev is the creation of such a “slim” library for Gambit to replace the default feature-full library. Gambit’s “tree shaker” would also help to eliminate the unused procedures of the slim library (unfortunately this only works for “whole program” compilation, so separate compilation would only be used for the development phase).
Anyway, if this interests you please let me know.
Marc
On Jan 1, 2016, at 11:44 AM, Blake McBride blake@mcbride.name wrote:
Just some opinions.
asmjs.org defines a portable subset that allows JS platforms to compile into very fast code. Targeting that subset, and within their spec, is probably a good idea.
JS has, and is becoming increasingly, a very, very important platform. With ajax and rest services, code increasingly independent from the back-end is being developed. So, in a very important sense, JS has become its own platform, just like X86, and X86_64, along with Linux, Windows, Mac, etc.
Many apps consist of two major parts: the back-end processing, and the front-end human interface. While one can write the back-end processing in any of dozens of languages targeting X86 or a VM, and the OS, there is really only one target for the other half - the human interface - and that is JS.
While many languages are now targeting the JS platform (I am using the word "platform" to mean X86, X86_64, either, plus the OS) including, believe it or not, Smalltalk, there are few that can, IMO, be used in a real world application for several reasons. But, these issues will likely be resolved soon.
With respect, Gambit, at least in the past, was in many ways another toy solution - one with a functioning "Hello world" application but missing important features that make it usable in a real-world situation. I would love to be a part of that solution, but alas, life doesn't offer me that level of freedom. I am only capable of using a system that is reportedly working, reporting bugs, making small adjustments, and providing feedback. Regardless of my attitude, preferences, and intentions, I cannot provide more.
Look at node (JS for the back-end) and its vastly increasing popularity. Since developers are being forced to use JS on the front-end, it's bleeding over to the back-end. I think solutions that take JS seriously at this stage will prosper greatly.
Now, having said all that, I would love to see Gambit target JS as a high-priority, first-class target. If that were the case, I would be happy to contribute what I stated above.
Thanks!
Blake McBride
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