This evening, I've updated the list of tickets at http://trac.common-lisp.net/armedbear/report/1. Assigning the tickets in the list to specific releases makes the roadmap view (http://trac.common-lisp.net/armedbear/roadmap) nicely indicate our progress.
If you have an issue, but it's not mentioned in the list of tickets, 2 things can be the case:
1) We don't know about your ticket, or have forgotten about it 2) The ticket is more of a general thing we need to keep doing (there's no specific goal or target to be achieved)
In the second case, we can't file a ticket, because I would like to be able to close tickets some day. As you'll find, I've assigned some tickets to the milestone "too-vague". If we can't come up with more specific descriptions, I think we'll have to reclassify these to "reminder" type tickets. (We don't have those, yet, btw.)
So, I'd like to hear your comments on missing tickets.
I'd also like to discuss assignment of tickets to milestones. This won't mean that they will be fixed by then, but they *are* meant to be a guideline for priorities and hopefully an indication for interested users on which larger issues we intend to tackle and roughly when.
Bye,
Erik.
On Tue, Nov 3, 2009 at 11:12 PM, Erik Huelsmann ehuels@gmail.com wrote:
This evening, I've updated the list of tickets at http://trac.common-lisp.net/armedbear/report/1. Assigning the tickets in the list to specific releases makes the roadmap view (http://trac.common-lisp.net/armedbear/roadmap) nicely indicate our progress.
If you have an issue, but it's not mentioned in the list of tickets, 2 things can be the case:
- We don't know about your ticket, or have forgotten about it
- The ticket is more of a general thing we need to keep doing
(there's no specific goal or target to be achieved)
In the second case, we can't file a ticket, because I would like to be able to close tickets some day. As you'll find, I've assigned some tickets to the milestone "too-vague". If we can't come up with more specific descriptions, I think we'll have to reclassify these to "reminder" type tickets. (We don't have those, yet, btw.)
So, I'd like to hear your comments on missing tickets.
I don't know if it can be a ticket, and it's a long-term goal, but: we have had many discussions about save-image and serialization; now that we are addressing startup times too, it occurred to me that this stuff is somehow all connected together. Serialization is useful to restore the state of variables; however most of the startup time is not spent assigning variables, but loading and instantiating compiled functions through reflection, and serialization would probably not help much in this case. What could help is, as per Ville's idea, create a big "loader" class which references compiled function classes directly by name in its bytecode (and thus does not use reflection), and use that to restore compiled functions in block when you load the saved image. This should hopefully speed up startup times quite dramatically. However, it's not an easy goal since it depends on some other things:
- compiled functions should not call loadCompiledFunction but delegate loading of other classes to their classloader, so that when loaded immediately abcl will provide a temporary classloader, when loaded by the big loader class they will all use the same classloader completely avoiding reflection. - remember/recall should be redesigned since now it allows to load a given function only once. - serialization alone is not easy to implement and get right, and requires us to make choices: should serialization only be used for save-image, or given that we implement it do it so that it is more fine-grained (to allow serialization of single Lisp objects, e.g. for remoting or short-term persistence)? The first case is easier, the second one is way cooler ;) - and probably other things I'm not considering.
Alessio
Just a comment
most of the startup time is not spent assigning variables, but loading and instantiating compiled functions through reflection, and serialization would probably not help much in this case. What could help is, as per Ville's idea, create a big "loader" class which references compiled function classes directly by name in its bytecode
Other java lisps define each Primitive execute as a single static function in a trampolines file. The Primitive itself just calls the static function in the trampolines ( they are named in such a way it is easy to reference the class that holds nothing but static functions).. In compiled code there is never any reason to reference the Primitive or even the Symbol that uses it. The compiled code is simply a series of static functions calling static functions
Here is an example:
http://larkc.svn.sourceforge.net/viewvc/larkc/trunk/platform/src/com/cyc/too...
notice on line 124 "bell_next_float" this is defined #'BELL-NEXT-FLOAT on line 505 as: declareFunction(myName, "bell_next_float", "BELL_NEXT-FLOAT", 1, 0, false); the 1-0-false means require 1 required arg.. 0 optiona args.. and donent allow &rest
Now whenever code is going to use #'BELL-NEXT-FLOAT It knows it can INVOKE-STATIC "com.cyc.tool.subl.jrtl.translatedCode.sublisp.math_utilities" "bell_next_float" consuming one stack arg.
On Thu, Nov 5, 2009 at 1:45 PM, logicmoo@gmail.com wrote:
Just a comment
most of the startup time is not spent assigning variables, but loading and instantiating compiled functions through reflection, and serialization would probably not help much in this case. What could help is, as per Ville's idea, create a big "loader" class which references compiled function classes directly by name in its bytecode
Other java lisps define each Primitive execute as a single static function in a trampolines file. The Primitive itself just calls the static function in the trampolines ( they are named in such a way it is easy to reference the class that holds nothing but static functions).. In compiled code there is never any reason to reference the Primitive or even the Symbol that uses it. The compiled code is simply a series of static functions calling static functions
This is a useful technique to speed up compiled code. It could also speed up startup times as long as you don't invoke such primitives from interpreted code during startup (in such a case, you would need to load and install the appropriate Primitive subclass to invoke it, or use a generic subclass that invokes the appropriate static method through reflection).
Alessio
On Thu, Nov 5, 2009 at 1:45 PM, logicmoo@gmail.com wrote:
Other java lisps define each Primitive execute as a single static function in a trampolines file. The Primitive itself just calls the static function in the trampolines ( they are named in such a way it is easy to reference the class that holds nothing but static functions).. In compiled code there is never any reason to reference the Primitive or even the Symbol that uses it. The compiled code is simply a series of static functions calling static functions
From: "Alessio Stalla" alessiostalla@gmail.com
This is a useful technique to speed up compiled code. It could also speed up startup times as long as you don't invoke such primitives from interpreted code during startup (in such a case, you would need to load and install the appropriate Primitive subclass to invoke it, or use a generic subclass that invokes the appropriate static method through reflection).
Indeed
Here is an example of the transition I am working on ----------------------------------------------------------------------------
// ### char< private static final Primitive CHAR_LESS_THAN = new Primitive("char<", "&rest characters") { @Override public LispObject execute() throws ConditionThrowable { return error(new WrongNumberOfArgumentsException(this)); } @Override public LispObject execute(LispObject arg) throws ConditionThrowable { if (arg instanceof LispCharacter) return T; return type_error(arg, SymbolConstants.CHARACTER); } @Override public LispObject execute(LispObject first, LispObject second) throws ConditionThrowable { return first.charValue() < second.charValue() ? T : NIL; } @Override public LispObject execute(LispObject[] args) throws ConditionThrowable { final int length = args.length; char[] chars = new char[length]; for (int i = 0; i < length; i++) { chars[i] = args[i].charValue(); } for (int i = 1; i < length; i++) { if (chars[i-1] >= chars[i]) return NIL; } return T; } };
Converts to: --------------------------------------------------------------------------------------------
class CharacterFunctions { static final String myName= "org.armedbear.lisp.CharacterFunctions";
public final static LispObject primitive_CHAR_LESS_THAN_execute_1_0_false(LispObject req0) { req0.charValue(); // throws the type_error in LispObject (LispCharacter overrides) return T; }
public final static LispObject primitive_CHAR_LESS_THAN_execute_2_0_false(LispObject req0, LispObject req1) { return req0.charValue() < req1.charValue() ? T : NIL; }
public final static LispObject primitive_CHAR_LESS_THAN_execute_0_0_true(LispObject[] args) { final int length = args.length; if (length==0) return error(new WrongNumberOfArgumentsException(LispThread.currentMethod())); char[] chars = new char[length]; for (int i = 0; i < length; i++) { chars[i] = args[i].charValue(); } for (int i = 1; i < length; i++) { if (chars[i-1] >= chars[i]) return NIL; } return T; }
static void declare_character_functions() { declareFunction(myName,"primitive_CHAR_LESS_THAN_execute_1_0_false","CHAR<",1,0,false); declareFunction(myName,"primitive_CHAR_LESS_THAN_execute_2_0_false","CHAR<",2,0,false); declareFunction(myName,"primitive_CHAR_LESS_THAN_execute_0_0_true","CHAR<",0,0,true); ...... }
2009/11/6 logicmoo@gmail.com:
Other java lisps define each Primitive execute as a single static function in a trampolines file. The Primitive itself just calls the static function in the trampolines (
This is a useful technique to speed up compiled code. It could also speed up startup times as long as you don't invoke such primitives
Indeed
The translation looks like there's no longer a Primitive that extends LispObject. How do you then pass the primitive as one? Like passing EQ/EQUAL/EQUALP to FIND/POSITION family of functions? Or user-defined functions that take functions, and you don't know the type and must box it (aka pass it as a LispObject)? Do we generate wrappers on the fly?
----- Original Message ----- From: "Ville Voutilainen" ville.voutilainen@gmail.com To: dmiles@users.sourceforge.net Cc: "Armed Bear" armedbear-devel@common-lisp.net Sent: Thursday, November 05, 2009 9:57 PM Subject: Re: [armedbear-devel] 0.18 and beyond
2009/11/6 logicmoo@gmail.com:
Other java lisps define each Primitive execute as a single static function in a trampolines file. The Primitive itself just calls the static function in the trampolines (
This is a useful technique to speed up compiled code. It could also speed up startup times as long as you don't invoke such primitives
Indeed
The translation looks like there's no longer a Primitive that extends LispObject.
No longer a "requirement" for the compiler to *have to* use a Primitive
Do we generate wrappers on the fly?
Correct As you foresee, for cases like #'APPLY/#'FUNCALL etc and from the interpreter it is done on the fly.
declareFunction(myName,"primitive_CHAR_LESS_THAN_execute_1_0_false","CHAR<",1,0,false); declareFunction(myName,"primitive_CHAR_LESS_THAN_execute_2_0_false","CHAR<",2,0,false); declareFunction(myName,"primitive_CHAR_LESS_THAN_execute_0_0_true","CHAR<",0,0,true);
There are 5 objects created:
One Symbol: Symbol c = intern("CHAR<",pkg);
Three Functors: f0 = declareFunction(myName,"primitive_CHAR_LESS_THAN_execute_1_0_false","CHAR<",1,0,false); f1 = declareFunction(myName,"primitive_CHAR_LESS_THAN_execute_2_0_false","CHAR<",2,0,false); f2 = declareFunction(myName,"primitive_CHAR_LESS_THAN_execute_0_0_true","CHAR<",0,0,true);
OnePrimtive subclass called FunctorIndexer that indexes the call sigs to the best ForwardFunctor.
OnePrimtive is placed inside the symbol-function.
So far we have made 5 objects ------------------------------------------------------------------------------------------ On first use: lets say:
(funcall 'char< #\a #\b #\c)
thats 3 args ins .. the best match is f2 -
This transistion is handled in here: http://larkc.svn.sourceforge.net/viewvc/larkc/trunk/platform/src/com/cyc/too...
It creates one class that implments processArgs(LispObject r0,LispObject r1,LispObject r2) And loads the arity3Cache of the FunctorIndexer which physically calls the static function:
return primitive_CHAR_LESS_THAN_execute_0_0_true(new LispObject[]{r0,r1,r2});
----------------------- So far we have made 6 objects -----------------------
Note though this machinery is only used in FUNCALL and interpreted code
When hitting the functor generator .. it should be considered "worse case"
But serves a better way case than hitting the reflection on line 62 of http://larkc.svn.sourceforge.net/viewvc/larkc/trunk/platform/src/com/cyc/too... (But this 2ndary wrapper technique still isn't that bad. )
How do you then pass the primitive as one? Like passing EQ/EQUAL/EQUALP to FIND/POSITION family of functions?
You find-or-create the Symbol like findSymbol("EQ",pgk).getSymbolFunction().execue(a0,a1,etc); like before.
".getSymbolFunction()" is where the new magic takes place
Or user-defined functions that take functions, and you don't know the type and must box it (aka pass it as a LispObject)?
Currently all execute(..,.,.,.) signatures take LispObject as before.
However with not much more work it is possible to make a cache of unboxed versions like...
public final static LispObject primitive_CHAR_LESS_THAN_execute_2_0_false_nobox(char req0, char req1) { return req0 < req1? T : NIL; }
Registered the same way to the interpreter and compiler:
declareFunction(myName,"primitive_CHAR_LESS_THAN_execute_2_0_false_unboxed","CHAR<",2,0,false);
Actually if you want, is even simplier to not go thru the explicit "registration process" but using Annotations
@InlineCompiler(symbolname="CHAR<", package="COMMON-LISP"); public final static LispObject primitive_CHAR_LESS_THAN_execute_2_0_false_nobox(char req0, char req1) { return req0 < req1? T : NIL; }
Here is the list of some improvements I see for ABCL in the future.
* Reduce number of Innerclass calling outerclass stack frames (by converting inassessable method calls to accessable) * Remove the subclassing of main Lisp class. It was an unneeded as a trick to emulate static importing * Decompile many of the library files such as format.lisp, pprint.lisp etc... convert to .java files * Convert slotted classes to using fields not arrays * Eliminate as much Array producing code as possible * Make the multipleValuesList code in LispThread use fileds instead * Create fast "unwind to" index in environmnets (storing last-known-binding-index int eh Symbol Objects themselves at times) * Make ABCL run on .NET VMs
2009/11/6 logicmoo@gmail.com:
Here is the list of some improvements I see for ABCL in the future.
- Decompile many of the library files such as format.lisp, pprint.lisp etc... convert to .java files
I don't believe we want to convert lisp code to java code in the future. The ongoing desire is to have less java and more lisp.
On Fri, 6 Nov 2009 07:47:10 +0200 Ville Voutilainen ville.voutilainen@gmail.com wrote:
2009/11/6 logicmoo@gmail.com:
Here is the list of some improvements I see for ABCL in the future.
- Decompile many of the library files such as format.lisp,
pprint.lisp etc... convert to .java files
I don't believe we want to convert lisp code to java code in the future. The ongoing desire is to have less java and more lisp.
Yes. Certainly less Java would make it easier to eventually target .NET (w/o using ikvm): another improvement in the list.
Staying in lisp for important library functions simply means getting better at compiling to the JVM.
2009/11/6 Matthew D. Swank akopa@charter.net:
Ville Voutilainen ville.voutilainen@gmail.com wrote:
I don't believe we want to convert lisp code to java code in the future. The ongoing desire is to have less java and more lisp.
Yes. Certainly less Java would make it easier to eventually target .NET (w/o using ikvm): another improvement in the list.
Parrot and native code are also on my (very) long-term list. ;)
----- Original Message ----- From: "Ville Voutilainen" ville.voutilainen@gmail.com To: swank.matthew@gmail.com Cc: "Armed Bear" armedbear-devel@common-lisp.net Sent: Friday, November 06, 2009 8:12 AM Subject: Re: [armedbear-devel] 0.18 and beyond
2009/11/6 Matthew D. Swank akopa@charter.net:
Ville Voutilainen ville.voutilainen@gmail.com wrote:
I don't believe we want to convert lisp code to java code in the future. The ongoing desire is to have less java and more lisp.
A big reason I was suggesting
.lisp-> .class -> .java
Would be to discover and remove any/all LispInteger/Finxums that have somehow snuck into the bytecode.
Since something like the core library code should be free for these things until we can get the compiler not to produce them.
Of course it's silly to expect the compiler to ever get rid of all Fixnums from generated bytecode :)
But I wanted to sound absurd right there.. here is why:
Take the output of a different lisp compiler of "format.lisp" into java..
http://larkc.svn.sourceforge.net/viewvc/larkc/trunk/platform/src/com/cyc/too...
Look at line 930 with horror as you can see how very far away it is from doing the right thing. you'd expect it to read instead as: for(int i=0;i<scale;i++) If this was a critical function .. you'd think they'd go back in rewrite it better.
ABCL's compiler lets hope doing _way_ better than this.. Imagine if it had places it wasn't?!! How much efficient could the core libraries could be If after the initial lisp->class->java some hand attention was given?
If we found out the bytecode emitted Lisp.readFromString(..)? Would we leave it like that?
Now realistically only when needed. and very important parts of the core. Since every compiler improvement will regenerate a new "lisp->class->java" The hand improvements should be allot less each time.
Erik often inspects the bytecode in the disassembler to make sure its at least reasonably sane. On many levels each compiled Primitive is pretty ok.. Once they are generated into an "app" sometimes might need to be looked out with more sets of human eyes. And the compiler reworked until and reworked until eventually the human eyes are no longer needed.
Hi Alessio,
On Wed, Nov 4, 2009 at 5:47 PM, Alessio Stalla alessiostalla@gmail.com wrote:
On Tue, Nov 3, 2009 at 11:12 PM, Erik Huelsmann ehuels@gmail.com wrote:
This evening, I've updated the list of tickets at http://trac.common-lisp.net/armedbear/report/1. Assigning the tickets in the list to specific releases makes the roadmap view (http://trac.common-lisp.net/armedbear/roadmap) nicely indicate our progress.
If you have an issue, but it's not mentioned in the list of tickets, 2 things can be the case:
- We don't know about your ticket, or have forgotten about it
- The ticket is more of a general thing we need to keep doing
(there's no specific goal or target to be achieved)
In the second case, we can't file a ticket, because I would like to be able to close tickets some day. As you'll find, I've assigned some tickets to the milestone "too-vague". If we can't come up with more specific descriptions, I think we'll have to reclassify these to "reminder" type tickets. (We don't have those, yet, btw.)
So, I'd like to hear your comments on missing tickets.
I don't know if it can be a ticket, and it's a long-term goal, but: we have had many discussions about save-image and serialization; now that we are addressing startup times too, it occurred to me that this stuff is somehow all connected together.
save-image by itself is a well defined functionality, I think. If there are any intermediate milestones which can be equally well defined, I'd like to file them too; however, "serialization" in general isn't good enough to file as a ticket. "serialization of ABCL LispNumber class descendants" would be more to the point, if that's what you would be trying to achieve. I think you get the picture.
Serialization is useful to restore the state of variables; however most of the startup time is not spent assigning variables, but loading and instantiating compiled functions through reflection, and serialization would probably not help much in this case. What could help is, as per Ville's idea, create a big "loader" class which references compiled function classes directly by name in its bytecode (and thus does not use reflection), and use that to restore compiled functions in block when you load the saved image. This should hopefully speed up startup times quite dramatically. However, it's not an easy goal since it depends on some other things:
Bye,
Erik.
armedbear-devel@common-lisp.net