lmj / lparallel

Parallelism for Common Lisp
http://lparallel.org
BSD 3-Clause "New" or "Revised" License
242 stars 29 forks source link

Add lockless queues for LispWorks. #6

Closed ska80 closed 11 years ago

ska80 commented 11 years ago

Tested with LispWorks Pro 6.1.1 (32 bit, linux).

This implementation is based on ccl-spin-queue.lisp using LispWorks SYSTEM:COMPARE-AND-SWAP.

Passes all tests with and without :LPARALLEL.WITH-STEALING-SCHEDULER pushed.

lmj commented 11 years ago

Thanks, I'll merge this soon. The queue I submitted to sbcl-devel might be faster even with precise gcs; I need to do some benchmarking. I might wind up code-generating all CAS queues.

lmj commented 11 years ago

The LispWorks documentation says,

"Confirm when quitting image" - Actions to be executed before the image quits. Every action must return non- nil as its first value, otherwise the quit will be aborted once the actions are complete.
"When quitting image" - Actions to be executed when the image quits, after success of the "Confirm when quitting image" actions.

I changed the action from "Confirm when quitting image" to "When quitting image", otherwise if LispWorks doesn't actually quit then lparallel will be in an invalid state. If you think the former is better then let me know.

I may have found a LispWorks bug related to CAS, so for now the lockless queue is not enabled by default. In any case I didn't see much improvement, did you? Since LispWorks doesn't achieve speedup for the Fibonacci benchmark, we may be dealing with time scales where lockless queues won't matter.

ska80 commented 11 years ago

I changed the action from "Confirm when quitting image" to "When quitting image", otherwise if LispWorks doesn't actually quit then lparallel will be in an invalid state. If you think the former is better then let me know.

Actually, "When quitting image" didn't work for me because it calls all its actions (in our case TRACK-EXIT) AFTER multiprocessing is stopped and BEFORE quitting and as a result it blocks waiting for "kernel" threads to exit forever never having a chance to run TRACK-EXIT. By default, threads created with BT:MAKE-THREAD (calling MP:PROCESS-RUN-FUNCTION) are started without :INTERNAL-SERVER flag described below (from LW docs):

When true, this indicates that the process is an "internal server", which means that it responds to requests for work from other processes. The main effect of this is that if the only processes that remain are "internal servers", nothing is going to happen, so LispWorks quits. The system marks some of the processes that it creates as "internal server".

On the other hand, "Confirm when quitting image" works fine because it calls its actions (TRACK-EXIT) BEFORE stopping multiprocessing and properly stops all "kernel" threads before quitting.

"Confirm when quitting image" will work in server-side applications when we just need to shut down all services and quit but may not work in a GUI application if a user decides to cancel quitting image. :)

I may have found a LispWorks bug related to CAS, so for now the lockless queue is not enabled by default. In any case I didn't see much improvement, did you? Since LispWorks doesn't achieve speedup for the Fibonacci benchmark, we may be dealing with time scales where lockless queues won't matter.

Yes, I noticed that too after running benchmarks. It runs almost two times slower than without CAS. I should have run benchmarks first before sending the patch. The Fibonacci example from the tutorial is very slow comparing to CCL and SBCL.

lmj commented 11 years ago

"Confirm when quitting image" should not be used because if any action in that list returns nil then things will be broken, and worse, broken in a way that is not immediately obvious (worker threads will no longer be regenerated after being killed).

These exit hooks were intended for convenience only, to relieve the user of a final end-kernel call. I wouldn't advise depending upon them in any serious sense. Do you have some situation where you cannot call end-kernel?

I suppose the idiomatic LW solution here would be to mark worker threads as internal servers. If this is considered at all, it should probably be done by adding :implementation-args to bordeaux-threads:make-thread rather than having lparallel bypass bordeaux-threads:make-thread.

ska80 commented 11 years ago

"Confirm when quitting image" should not be used because if any action in that list returns nil then things will be broken, and worse, broken in a way that is not immediately obvious (worker threads will no longer be regenerated after being killed).

Agreed.

These exit hooks were intended for convenience only, to relieve the user of a final end-kernel call. I wouldn't advise depending upon them in any serious sense. Do you have some situation where you cannot call end-kernel?

I find it useful for debugging purposes in a server-side application and when I just wanted to quit the image without stopping "kernel" threads. Before sending the patch with 'define-action' I typed END-KERNEL before quitting and it worked. I guess I could just add END-KERNEL to my shutdown function.

I suppose the idiomatic LW solution here would be to mark worker threads as internal servers. If this is considered at all, it should probably be done by adding :implementation-args to bordeaux-threads:make-thread rather than having lparallel bypass bordeaux-threads:make-thread.

I actually have a branch of bordeaux-threads (dev branch) to implement this feature that sets :internal-server flag and it works fine with lparallel, I can't just figure out how to make or name or pass it properly for other implementations that don't have this feature.

lmj commented 11 years ago

I had in mind

(bordeaux-threads:make-thread #'f :implementation-args (or #+lispworks '(:internal-server t)))

though I'm ambivalent about it. I think LW should behave as I expected, with "When quitting image" being run before multiprocessing shuts down, or otherwise LW should provide another hook. All other popular implementations have such a hook.

ska80 commented 11 years ago

I think LW should behave as I expected, with "When quitting image" being run before multiprocessing shuts down, or otherwise LW should provide another hook. All other popular implementations have such a hook.

I am going to report this as a bug tomorrow.

ska80 commented 11 years ago

Hi James,

LW guys will consider adding extra action lists to handle starting and stopping multiprocessing in the following releases.

lmj commented 11 years ago

If you still wish to avoid the end-kernel call, the aforementioned addition to bordeaux-threads seems reasonable if you wanted to pursue it.

It would have been nice if the define-action in lparallel turned into a prescient fix, but I didn't expect it due to backward compatibility with the current hooks.

ska80 commented 11 years ago

Hi James,

Thank you for your support.

In a delivered app I always call END-KERNEL explicitly so there is no problem. As for development stage I ended up using "Confirm when quitting image" action as it works for my use case when I just need to quit an image without canceling :) Quicklisp allows to keep local custom systems that override the installed ones so I just use my local git branch. I hope LW guys will fix the issue and "When quitting image" action will work as expected.