freenode/#clasp - IRC Chatlog
Search
4:28:02
Bike
it'll do more iteration than going through a stack of just relevant entries like in sicl, i imagine.
4:28:42
beach
Whereas in Common Lisp, an exit point would just not rely on callee-saves registers, so that the rest of the stack can just be abandoned.
4:28:56
Bike
I should also mention that there's an alternate implementation of C++ exceptions that uses chains of setjmp/longjmp buffers, which conceptually seems much more like what SICL does
4:29:22
Bike
since there's a small runtime cost whenever you enter a frame that's exception-relevant
4:30:48
drmeister
We could use that Bike. We could compile all of our C++ code using SJLJ and generate SJLJ code in clasp - couldn't we?
4:31:33
Bike
We would also need anyone who links any extension C++ code to use SJLJ for it too, right? Including libraries they may not be able to compile because they're part of the system or proprietary.
4:31:48
drmeister
Hmm, we would have to compile everything with that - llvm, clang, GMP, libz... anything in C++
4:32:55
drmeister
Yeah - we don't want to go that direction. We just need to reduce the number of unwinds we do.
4:34:23
Bike
i think avoiding c-w-v-b is probably going to be more of a win than rewriting code, generally, so i'm going to focus on that
4:34:45
drmeister
I'm not convinced that it has to. It seems to me that the regular return path will always be faster than unwinding - so we should use it for the common case. It would surprise me if that makes the code less readable.
4:35:22
Bike
if you could find examples that don't involve c-w-v-b that would be interesting, drmeister. like the one in inlining
4:36:41
Bike
presumably the full thing is (block ... more code here (let ((*mumble* ...)) (return ...)) yet more code...)
4:37:40
beach
The RETURN could involve the value of *MUMBLE*, and the code after the LET must be abandoned.
4:37:51
drmeister
Doesn't it? I'm not convinced that I'm right - and you are a more subtle common lisp programmer than I.
4:38:07
Bike
i don't really see any reason to rewrite ones with special variable bindings since we should be able to just eliminate c-w-v-b
4:40:15
drmeister
Backtraces are really deep, excessive unwinding, forcing lexical variables into closures are three problems with it.
4:44:18
drmeister
So we are going to move in the direction of eliminating c-w-v-b. We'd like to do it in a way that segues into how cleavir2 does things. I don't know what that is - but Bike does - so I'll leave it to him.
4:54:32
drmeister
The backtraces are really, really deep and I have to truncate them to get anything to display.
12:35:22
drmeister
I can only do about 2,000 unwinds/second on this machine with nothing else going on.
12:36:25
drmeister
scymtym: Hi - yeah - but I think our compiler can get rid of 90% of what we are throwing right now.
12:37:08
drmeister
Currently (block foo (let ((*special* ...)) ... (return-from foo ...) ...)) throws an exception.
12:38:25
drmeister
Currently I think Clasp's compiler budget is dominated by eclector because of this.
12:39:34
drmeister
That's not knocking eclector - it's a very common pattern. We need to compile it better.
12:40:04
drmeister
I think it's true in general. It's really hard to measure this. It's a "death by a thousand cuts".
12:41:35
scymtym
yeah, i see. i didn't take it as criticizing eclector. that said, i may have to look into performance issues at some point anyway
12:42:24
drmeister
I think something to keep in mind is "am I using the regular return path for the common case".
12:43:06
drmeister
Also, we have a tool now that lets us find the code that is doing the most throwing.
12:43:50
drmeister
In C++ it's really obvious when you are returning the normal way and when you are throwing an exception.
12:45:55
drmeister
Indeed ... the (block foo (let ((*special* ...)) ... (return-from foo ...) ...)) pattern currently throws because the return-from crosses a function boundary. Once Bike is done with it - it wont
12:48:24
drmeister
In that flame graph above - most of the time is lost throwing exceptions in eclector, cleavir and asdf.
12:53:21
scymtym
the combination of special variables and many embedded state machines (i.e. TAGBODY+GO/RETURN) is probably the main reason for this in eclector
14:55:23
Bike
Another ridiculous aspect of this is that the machinery that parses unwinding info is complex enough that some things could be better expressed with...... exceptions
14:57:44
Bike
drmeister: Since the thread local exception space won't include pointers to any Lisp objects, should I just use a regular thread-local variable instead of putting it in our thread local data structure gizmo?
14:59:44
Bike
well actually there's a bunch of non lisp things in there already, so now i'm not sure what it's for exactly
15:34:41
drmeister
Bike: If there are no pointers to lisp objects then you can put it in a thread-local variable - yes. That will save you an indirection.
15:35:18
drmeister
Everything accessible from my_thread->xxx requires following a pointer - but you can put lisp objects in the my_thread->xxx area.
15:37:41
drmeister
I'm keeping them both running until llvm10 or llvm11 when we can get rid of "object" if we choose.
15:38:22
drmeister
Bike: I just put them there - it didn't seem like a big deal. It would be better to put them somewhere else.
15:38:53
drmeister
https://github.com/clasp-developers/clasp/blob/dev/include/clasp/gctools/threadlocal.fwd.h#L79
15:39:21
drmeister
https://github.com/clasp-developers/clasp/blob/dev/include/clasp/gctools/threadlocal.h#L49
15:42:39
drmeister
I've made a point of keeping all the thread local stuff accessible through my_thread (ThreadLocalState*) and my_thread_low_level (ThreadLocalStateLowLevel*)
15:43:24
drmeister
It is - but when I started thread local storage support was still kind of squirrely - so I didn't want a lot of different thread local objects to deal with.
15:43:57
drmeister
Also, we need to store some Common Lisp pointers in the thread local storage. That is best done by allocating it at the top of the stack and letting the GC deal with it that way.
15:45:18
drmeister
So how about we move the POD/C++ objects from ThreadLocalState to ThreadLocalStateLowLevel and put them directly in thread local storage?
15:45:50
drmeister
It's real simple. We just move the field over and the compiler will complain about every my_thread->_foo access and we change it to my_thread_low_level._foo.
15:46:46
drmeister
Then we avoid an indirection and the GC doesn't have to scan these values and potentially confuse them with pointers. That's less of a problem in our 64-bit world.
15:48:03
drmeister
We can also take them out of ThreadLocalStateLowLevel entirely and have a bunch of thread local values.
15:48:23
Bike
means more #including, and having things only one system needs being stored in some random other file
15:48:52
Bike
I mean, look at the threadlocal structure now - it's just a potpourri of random things
15:49:00
drmeister
The ThreadLocalState struct for Common Lisp pointers is convenient for allocating it at the top of the stack (stack allocates down).
15:49:54
drmeister
But when I wasn't sure that I could put anything but a pointer in thread local storage - it made sense to have a 'god' object. Now it makes less sense.
15:53:55
drmeister
I can't tell you how much of a comfort my_thread->xxx is. I immediately know that it's thread local.