NPRG051 Advanced C++ Programming 2016/17 Assignment 2
Topic Async I/O B-Tree server
Motivation Typical database: tree indices performance based on disk speed synchronization speed on parallel access minimal CPU load
Motivation Typical database: tree indices B-trees to save I/O operations performance based on disk speed cca. O(h seek) synchronization speed on parallel access locking minimal CPU load 1 CPU is more than enough
Target Assignment: Server that provides access to basic operations on a B-tree. in 1 thread asynchronous processing of client requests
Asynchronous processing 1 Clients Server Storage op 1 1 recv 2 2 recv op 2 ok 1 ok 2 poll poll poll 1 2 2 1
Implementation How to process requests parallely in 1 thread? event loop AIO Why not use more threads?
Implementation How to process requests parallely in 1 thread? event loop AIO Why not use more threads? That provides almost no extra throughput! bottleneck is elsewhere 1 thread saves system resources hand scheduling is often better Synchronization overhead! only 1 thread can accept requests locking required deadlocks...
Implementation How to process requests parallely in 1 thread? event loop AIO Why not use more threads? That provides almost no extra throughput! bottleneck is elsewhere 1 thread saves system resources hand scheduling is often better Synchronization overhead! only 1 thread can accept requests locking required deadlocks... Common motivation: C10K problem
Asynchronous processing Server: Client: send(s, "get X"); reply = recv(s); for(;;) { active=poll(sockets); foreach(s in active) { req=recv(s); handle(req); } }
Asynchronous processing Server: Client: send(s, "get X"); reply = recv(s); for(;;) { active=poll(sockets); foreach(s in active) { req=recv(s); handle(req); } }
Asynchronous processing Server: Client: send(s, "get X"); reply = recv(s); for(;;) { active=poll(sockets); foreach(s in active) { req=recv(s); handle(req); } }
Asynchronous processing Server: Client: send(s, "get X"); reply = recv(s); for(;;) { active=poll(sockets); foreach(s in active) { req=recv(s); handle(req); } }
Asynchronous processing Server: Client: send(s, "get X"); reply = recv(s); for(;;) { active=poll(sockets); foreach(s in active) { req=recv(s); handle(req); } }
Podpora v OS Sockets: POSIX fcntl (fd, F_SETFL, O_NONBLOCK); poll(2); Windows ioctlsocket (fd, FIONBIO, &a); select(); File I/PO: Linux/glibc aio(7) (!!!) UNIX aio(4) Windows OVERLAPPED portable Boost.Asio, libeio For this assignment we provide a harmless mock wrapper.
aio struct aiocb { int aio_fildes; off_t aio_offset; size_t aio_nbytes; void *aio_buf; struct sigevent aio_sigevent; //... }; aio_read(aiocb*); aio_write(aiocb*); aio_cancel(aiocb*); aio_suspend(aiocb**, n, timespec); aio_error(aiocb*); aio_return(aiocb*);
AdvC++AIO using IO=ACIO<...>; size_t block_size = IO::block_size; // == k*512 IO io("file.dat"); io.resize(10000); IO::op o(blockid, dataptr); io.read(&o); io.write(&o); io.poll(io_list, 1.5, false); io.finish(&o); IO::req req; io.recv_request(&req); req.value=12345; io.send_reply(&req); io.poll(io_list, 2.0, true);
Functionality requirements program implements a B-tree that behaves just like map<uint64_t, uint64_t>::operator[] clients send requests: read req.type==io::req::read input: req.key output: req.value (default 0) write IO::req::write, 2 inputs, same output erase IO::req::erase, output 0
Functionality requirements program implements a B-tree that behaves just like map<uint64_t, uint64_t>::operator[] clients send requests: read req.type==io::req::read input: req.key output: req.value (default 0) write IO::req::write, 2 inputs, same output erase IO::req::erase, output 0 confirmed data must be persistent (!) order of answer content should be consistent e.g.: write(10,5), write(10,6), read(10,5) more requests must be handled in parallel it is important to limit the amount of requests handled at the same time (!!)
Testing Read through the demo (it serves as testing framework documentation) ACIO has 2 implementations mock in-memory with artificial I/O delays aio POSIX AIO, good for testing on larger data (actually writes blocks to disk) Testing client parameters: Count and frequency of incoming requests bursts
Submitting In main.cpp, add your implementation to run_btree_server. You can add your own btserver.cpp a btserver.hpp. Submit to SIS.
Evaluation correctness code quality no errors correct answers to clients portability (relative) correctly submitted files structure formatting documenting commentary (!!) implementation speed on mock implementation ability to limit RAM usage (and withstand request floods)
Questions? Q&A
Questions? Q&A Good questions: How to survive request flood? What is it good for?
Questions? Q&A Good questions: How to survive request flood? save RAM, clients can wait What is it good for? PostgreSQL?