====== Read Copy Update ====== ===== Task assignment ===== Imagine that you are developing a multi-threaded application which proceeds concurrently many read and write requests on a shared data structure. Your goal is to speed up the application and achieve processing of 50k write operations per second and as much read operations as possible. Steps: - Download a naive implementation with mutexes from git repository: git clone https://gitlab.fel.cvut.cz/matejjoe/list_mutex.git - Compile the program (simply run ''make'' in list_mutex directory) - Run the program (''./list_mutex '') and see how slow the program is * The program creates one write thread and specified number of reader threads and prints achieved read and write operations per second * Since a priority of the mutex cannot be set, it may happen that writes or reads are not performed at all - Replace the mutexes with rwlocks and measure improvement * Use lock priority, to guarantee that given amount of writes will be performed - Replace list with liburcu (userspace RCU library) list and measure improvement * See hints below - For rwlocks and librcu, measure reads with various number of reader threads and plot the results into a graph (for example from one to twenty readers) – use **sufficient hardware** (at least 8 cores -- total number of threads shouldn't be larger than physical cores) – you can use some of remote servers such as ritchie.ciirc.cvut.cz (CTU login with KOS password). It is sufficient to measure just average values from let say 10 iterations. - Commit the graph and code changes **to your tutor** (then into upload system). The graph should look roughly like this: {{ :courses:b4m36esw:labs:rcu.svg.png?400 | sample graph}} ==== pthread rwlocks ==== The rwlock is part of pthread library. You could be interested in: * [[https://linux.die.net/man/3/pthread_rwlock_init|pthread_rwlock_init()]] * [[https://linux.die.net/man/3/pthread_rwlock_wrlock|pthread_rwlock_wrlock()]] * [[https://linux.die.net/man/3/pthread_rwlock_rdlock|pthread_rwlock_rdlock()]] * [[https://linux.die.net/man/3/pthread_rwlock_unlock|pthread_rwlock_unlock()]] * [[https://linux.die.net/man/3/pthread_rwlockattr_init|pthread_rwlockattr_init()]] * [[http://man7.org/linux/man-pages/man3/pthread_rwlockattr_setkind_np.3.html|pthread_rwlockattr_setkind_np()]] – reader/writer priority – use PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP and don't forget to init atribute before (pthread_rwlockattr_init) This part should be quite straightforward. In short, replace all pthread_mutex_lock/unlock, and update initialization. Variables (also structures) in C created on the stack (e.g. local variables) are initialized by random values, therefore, make sure that you initialize any created structure before use. ==== Userspace RCU ==== The liburcu should work on Linux, Cygwin and MacOS X. You can obtain [[http://liburcu.org|liburcu]] from distribution repository or compile yourself. I would recommend you compiling the library because libraries in repositories are usually a bit old. In order to compile and use library do following: * Download and compile library * wget --no-check-certificate http://www.lttng.org/files/urcu/userspace-rcu-latest-0.10.tar.bz2 * tar -xjf userspace-rcu-latest-0.10.tar.bz2 * cd userspace-rcu-0.10.2/ * ./configure --prefix=/home/me/somefolder/mybuild/output/target replace the prefix by something meaningful on your system -- this is a place where you will install the library and link with the application afterwards * make -j8 * make install * In ./doc/examples/list/ are useful examples * Add library into Makefile * add ''-lurcu'' or other rcu flavour into ''$LIBS'' (before ''-lpthread'') * add path to include directory ''-I/home/me/somefolder/mybuild/output/target/include'' into ''$INCLUDES'' * add path to library directory ''-L/home/me/somefolder/mybuild/output/target/lib'' into ''$LFLAGS'' Which steps should I do? - Get liburcu library - Read [[https://liburcu.org/#quick-start-guide|quick start guide]] - Look into examples folder (doc/examples/list/) to see how can you use the library - Chose user-space rcu flavor ([[https://lwn.net/Articles/573424/#URCU%20Flavors|check table with requirements]] and [[https://liburcu.org/#quick-start-guide|quick start guide]]) - Integrate it into the application (you can do it directly in main or in list implementation -- I recommend you the latter) - Update esw_node structure -- add two elements (struct cds_list_head and struct rcu_head required by rcu implementation) - Replace declaration of list (use CDS_LIST_HEAD macro) - Implement required functions (take inspiration from examples) -- you don't have to implement not used operations - Don't forget to put requred library calls -- depending on selected flavour, you have to insert some of these: rcu_read_(un)lock, synchronize_rcu, rcu_(un)register_thread, rcu_quiescent_state, (sys_membarrier) - Measure speed-up ==== Supplementary materials ==== [[http://www.compsci.hunter.cuny.edu/~sweiss/course_materials/unix_lecture_notes/chapter_10.pdf|Article about threads and various types of locks]] [[https://lwn.net/Articles/573424/|Article about Userspace RCU library]] [[http://www.efficios.com/pub/rcu/urcu-main.pdf|User-Level Implementations of Read-Copy Update]]