freenode/#lisp - IRC Chatlog
Search
7:32:39
no-defun-allowed
The code that calls a generic function would be replaced with a call to the last effective method (with a prologue that calls the generic function as usual, if the last method is no longer applicable).
7:33:40
no-defun-allowed
It has been done on normal CPUs with Smalltalk, Self, Java and JavaScript at the least; so it may not be necessary to provide any processor support.
7:34:33
beach
The technique I was thinking of consists of using type information in the caller to create a call-site-specific discriminating function, and change it when the generic-function changes.
7:35:59
no-defun-allowed
The polymorphic inline cache described in <https://bibliography.selflanguage.org/_static/pics.pdf> is a bit closer to your technique, but it still picks the most common methods from runtime information.
7:38:31
no-defun-allowed
The only support that needs is that you need to be able to overwrite the call site, but that's not a big deal; and also doable on stock hardware.
7:48:44
no-defun-allowed
The one idea I have which may be better done in hardware (which gilberth gave me) is computing the address of an element in arrays of different element types when performing a generic AREF. (SBCL calls that "hairy", which is much more fun to say than "generic" though.)
7:58:49
beach
no-defun-allowed: I am thinking that things like AREF and CAR/CDR are usually done in a loop. So then, it is possible to make the type of the object a loop invariant.
8:00:33
beach
Plus, application writers who care that much about performance would stick in a type declaration that could then be verified quite easily.
8:02:23
beach
Speaking of which, I am convinced that Common Lisp implementations and their compilers are full of "optimizations" that are basically useless.
8:04:20
beach
In the case of SBCL NIL, it makes it necessary to do two tests in a loop over a list, rather than a single test, in order to determine the end.
8:05:14
beach
Though stassats assures me that there is no performance penalty for making two tests rather than one.
8:07:18
aeth
What I'm more concerned about that maintenance is compilation times. A lot of compilers are incredibly slow to get that extra speedup at the end, and it's questionable if it's worth it, especially if you translate that sort of approach to a CL compiler, which will be compiling more. (Yes, CL has optimization levels, but they're not that flexible)
8:08:05
aeth
People have complained about SBCL compilation times, but I don't really see that, except with a few edge case libraries (is Ironclad still really slow?)
8:08:55
beach
Especially for Common Lisp where the maintenance burden is a real problem, given the limited resources we have.
8:19:58
pfdietz
You don't want to make the generated code for car and cdr check if the thing is nil. That way lies madness. So what's the alternative?
8:21:49
pfdietz
You do car and cdr a lot in a lisp program, much more than you do symbol-name, symbol-plist, symbol-value, (typep ... 'symbol), etc.
8:23:04
beach
I claim it is not madness and that most CAR/CDRs are done in loops so the test has to be made anyway.
8:24:27
beach
So a single test for CONSP is almost always going to be TRUE which is the same number of tests you need in order to check for non-NIL atoms.
8:28:03
beach
But the thing that bothers me is that "optimizations" like this are put in as a result of intuition, and we know for a fact that our intuitions are frequently wrong when it comes to performance bottlenecks.
8:28:33
beach
And as a result we have, like I said, systems that are harder to maintain without any good reason for it.
8:29:44
aeth
I never directly call car or cdr... destructuring-bind is almost always the better solution.
8:36:38
pfdietz
"it's just like a macro lambda list" (looks) "the pattern is a sample object of the type to be decomposed."
8:40:37
pfdietz
I think we can safely criticize the language definition about this, though. It would have been ok for car/cdr of nil to be an error. I think this was inherited from Interlisp when CL was being defined. For that matter, punning () and false is also bad.
8:48:16
pfdietz
I see about 26000 occurrences of car in quicklisp. Picking a few at random, they don't seem to be in loops, at least not in tight ones. On the other hand that's not all that many tests to add, especially if many could be optimized out anyway. Real data on the impact would be interesting.
8:48:50
beach
And, like I said, even if it is not in a loop, you still need to test for non-NIL atoms.
8:48:56
pfdietz
That's just from a simple grep; it ignores implicit car/car from destructuring or looping forms.
8:49:40
pfdietz
If you just want an error to be signaled in that case you don't need an explicit check.
8:53:38
phoe
also all sequence operations have implicit car/cdr calls when called with list arguments
9:00:08
pfdietz
assoc is interesting. The spec explicitly says that a nil in an alist is ignored by assoc (and assoc-if, assoc-if-not). So (assoc nil '(nil (nil . a))) ==> (nil . a)
9:03:51
pfdietz
cons n.v. 1. n. a compound data object having two components called the car and the cdr.
9:08:21
phoe
"On PPC32 and X86-64, NIL is basically a weird CONS cell that straddles two doublenodes; the tag of NIL is unique and congruent modulo 4 (modulo 8 on 64-bit) with the tag used for CONS cells."
9:08:24
pfdietz
"The types cons, symbol, array, number, character, hash-table, function, readtable, package, pathname, stream, random-state, condition, restart, and any single other type created by defstruct, define-condition, or defclass are pairwise disjoint"
9:09:12
beach
phoe: That's the kind of "optimization" I was referring to earlier in this discussion.
9:10:53
pfdietz
So: the question becomes what is the actual impact of this design choice, vs. adding a check at each car/cdr. I suppose one could also trap car/car on nil and insert do the right thing in the trap handler.
9:12:21
beach
And I think the main impact is an increased maintenance burden. Especially since modern Common Lisp code uses standard objects where in the past lists were used.
9:13:50
phoe
well, unless your internal representation somehow manages to handle NIL being both a symbol and a valid argument to CAR/CDR, checks need to be somewhere
9:14:15
phoe
either it's internally represented as an atom and therefore CAR/CDR need to check, or it's internally represented as a cons and symbol accessors need to check
9:14:32
beach
So it is entirely possible that using a sub-optimal algorithm for generic dispatch is going to be much more detrimental to performance than having an explicit CONSP test.
9:15:09
pfdietz
Surely inlining the test like that makes sense here. car/cdr are not extensible with new methods, after all.
9:16:26
beach
pfdietz: That's not what I meant. I meant that, with a limited amount of person-power for maintenance, it would be wise to add complication where they have the most positive impact on performance.
9:17:51
beach
But I can also see how design choices like this were made when the implementation was a CLtL1 implementation so that lists were used a lot more than they are these days.
9:17:56
pfdietz
Sure. I was just wondering what the increased maintenance burden is buying, with reference to actual code in quicklisp. If it's not much, your argument becomes strong.
9:18:56
phoe
unless we have some sort of an implementation that we can modify and then benchmark on code
9:19:59
phoe
by "code" I mean both synthetic benchmarks that measure the performance impacts of raw operation and real applications that do little/moderate/lots of cons operations
9:23:00
pfdietz
One could shadow car/car in your code's packages (and, perhaps, library packages) with something that is inlined and does the checks, and see what effect that has.
9:23:31
pfdietz
To handle all the libraries, use a macroexpand hook to intercept defpackage and add the shadowing.
9:25:32
pfdietz
This would not handle car/cdr introduced in loops and mapping form, but as Beach argued those should not matter.
9:30:35
aeth
I essentially always just destructuring-bind and setf because d-b handles the edge cases in the way I want, unlike LOOP's destructuring.
9:31:15
pfdietz
destructuring-bind macroexpands to code that invokes sb-c::check-ds-list and sb-c::check-ds-list/&rest. I hope those are inlined.
9:33:46
Demosthenex
so i'm tinkering with clsql and the [] notation for sql, but emacs paredit isn't recoginzing those and indentation is a problem. any suggestions?
9:38:03
pfdietz
I have [ and ] mapped to paredit-open-parenthesis / paredit-close-parenthesis in the paredit-mode-map, but I use curry-compose-reader-macros where [ and ] represent function composition.
9:43:06
pfdietz
ISLISP does not have (car nil) and (cdr nil) being valid. It's mostly historical at this point, though.
9:45:15
ldbeth
Demosthenex: https://github.com/cireu/sly-el-indent here's some hack on sly (SLIME) to indent emacs lisp code, you might want to see if thoses hacks apply to your problem
9:48:50
pfdietz
trivia:ematch seems to generate faster code that destructuring-bind, for equivalent forms.
9:59:54
pfdietz
(scrolling up) The inlining of dispatch is made more exciting by the possibility that classes and methods can be dynamically modified. What if you need to recompile a function to reflect these changes while a call to that function is on the stack?
10:03:02
pfdietz
I figure you want to compile such functions with some sort of standardized stack frame, so you can return back to an unoptimized, or differently optimized, version of it if that happens.
10:03:26
aeth
pfdietz: I think, in general, you want to be fast assuming no dynamic redefinitions, although obviously permit the slow path to happen
10:03:46
aeth
most dynamic redefinitions will be user-defined, and infrequent, at least if you're assuming e.g. running in aloop
10:04:24
aeth
I do wonder how much of this is handled by CPU's branch prediction these days, though.
10:07:01
no-defun-allowed
Amazing, I was looking to see if Self had come up in #lisp before. Someone didn't reach for the search button.
10:10:09
no-defun-allowed
Perhaps it's too late for #lisp-ing, but I needed something to do while rebuilding SBCL with the higher internal-time-units-per-second so I can get less-than-awful variance with metering.
10:36:51
phoe
but I won't use it, because I just realized that I was trying to do something that did not make sense in general
11:10:34
aeth
phoe: In my Lisp, let* will just expand to destructuring-bind with an &aux since having both is unnecessary!
17:20:08
Josh_2
I have subclassed hunchentoot:acceptor and written a version oh acceptor-dispatch-request, is it possible to also use the default method ie (define-easy-handler ..) as well? currently I can only create new routes with the mechanism I have created, ideally I'd like both
17:29:11
gothnbass
Josh_2: I'm working on some code involving a subclass of acceptor myself right now, and... probably? To ask the annoyingly dumb question, what happens when you try it?
17:30:32
gothnbass
My use-case is relatively simple, though, so I just (setf tbnl:*dispatch-table* (list (create-regex-dispatcher "/url" 'function)...))
17:32:14
Josh_2
I might be able to modify my new route system to simply add a route like that to *dispatch-table*
17:32:29
gothnbass
OTOH, if you have any tips about round-tripping Unicode from Drakma to Neo4j and back, I'd love to hear 'em. Thought I'd solved it by handing Drakma a :content parameter to force it to pass through some pre-encoded data, but I'd swear Neo4j is mangling it, so I'm closing on table-flip.