Unikraft: Building extremely Fast, Specialized Unikernels the Easy Way
Jung, Alexander
In this talk we give an update on the Unikraft Linux Foundation open source project, a fully modular and librarized unikernel that aims to provide outstanding performance while making it easy to port off-the-shelf applications into unikernels. In particular, we will go into details how Unikraft (1) fully modularizes OS primitives so that it is easy to customize the unikernel and include only relevant components, (2) exposes a set of composable, performance-oriented APIs in order to make it easy for developers to obtain high performance and (3) aims for POSIX compatibility, already supporting over 130+ syscalls. In addition, there are ongoing efforts to integrate Unikraft into popular frameworks such as Kubernetes and Prometheus in order to finally bring the promise of unikernels to the mainstream. Our recent evaluation using off-the-shelf popular applications such as Nginx, SQLite, and Redis shows that running such applications on Unikraft results in a 30%-50% performance improvement compared to Linux guests. Unikraft images for these apps are around 1MB, require less than 10MB of RAM to run, and boot in around 1ms on top of the VMM time (total boot time 2ms-70ms). During the talk we will show a brief demo. Unikraft is Xen Project incubator project.
 Hello, in this talk I'll be giving an update on the Linux Foundation Open Source Project Unicraft, a fully modular and library sized Unicernel which aims to provide outstanding performance while making it easy to port off-the-shelf applications into Unicernels. My name is Alexander and I'm a PhD student at Lancaster University and I've been lucky enough to join the project over the last year to help bring more applications, tools and OS subsystems into the project as part of my research. Let's set the scene. Specialization is arguably the most effective way to achieve outstanding performance. This is seen classically with hardware. Most recently, with applications specific integrated circuits such as tensor processing units for machine learning, Intel's Movidius, a vision processing unit which enables on-demand computer AI vision, or FPGAs such as the NetFPGA for specialized line-rate network packet processing. But these solutions are costly, requiring additional overhead for the development, manufacturing and installation. Additionally, they are inherently scoped and can only perform a subset of compute by its very nature, making them inextensible once built. That's not to say that you can't reprogram them with new logic, but only defined by its architecture. When it comes to networking, performance can be achieved by crafting packets ready to go as is shown by standstone, leveraging the ability to write to the wire at line-rate when the traffic is known. Another direction is to add the required OS functionality from scratch for each target application, possibly by reusing code from existing operating systems. This is the approach taken by Clickos to support click modular routers and with Minicash to implement webcache, to just to name a few. In the case of Clickos and Minicash, the resulting images are very lean, have great performance and have small boot times. The big problem is that the porting effort is huge, and that it has to be mostly repeated for every single application or language. MirageOS, Erlang on Zen and runtime.js are examples of unikernels that have language-specific environments and have been created to provide performance optimizations for the language itself. So in the virtualization domain, unikernels are the golden standard for specialization, showing impressive results in terms of image size, boot times, memory consumption and high throughput. Some of the other benefits that come from having a single memory address space that will address later is the elimination of costly syscalls. Many of the benefits of specialization towards performance is the result of being able to hook the application at the right level of abstraction to extract the best performance, and this can be done in two ways. Transparenly, simply by compiling your application into a unikernel, you reap the benefits of lower boot times and memory consumption, or to modify the application itself to hook into these performance-oriented APIs. For example, a web server aiming to serve millions of requests per second can access a low-level, batch-based network API rather than going with a standard but slow socket. Unikernels' networking subsystem aims to decouple the device driver side, such as VertiOnet or NetFront, from the network stack or the low-level networking application. So can you do it with Linux? Well, existing monolithic OSes, such as Linux, have APIs for each component, but most APRs are quite rich and they have evolved organically, and so component separation is often blurred and aimed at achieving performance. For example, send file short-circuits the networking stack and storage stacks. We tried to quantify the API complexity in the Linux kernel and analyze dependencies between the main components. We used a program called Cscope to extract all the functions from all the sources of the kernel components, and then for each call, check to see if the function is defined in the same component or in a different one. In the latter case, we recorded the dependency. Here, you can see the dependencies between different subcomponents within Linux. But doing it with existing Unikernels is still troublesome. It still requires significant expert work to build and reuse existing OS components to target the specific application. Some Unikernel projects are not necessarily POSIX compliant, meaning that porting an application is non-trivial, having to provide the same kind of functionality to the application itself. And Unikernels, while smaller, are still monolithic, for example with Rump Run, that compiles against the BSD kernel, still provides a lot of internal OS primitives that are not necessarily used by the Unikernel itself. So starting from an existing Unikernel project is suboptimal, since none of the options were designed to support these inherent modularity and subsystem componentization. We opted for a clean slate API design approach, though we did reuse components from existing work when relevant. All components including operating system primitives, drivers, platforms, code and libraries should be easily added and removed when needed. Even API should be modular. In order to support existing or legacy applications and programming languages, we aim to provide POSIX compliancy as optional components as themselves, while still allowing for specialization under the API. So the kernel should be fully modular, and the kernel should provide a number of performance-oriented performance APIs. In contrast to classical OS work, we can roughly split between monolithic kernels which have great performance versus micro kernels that provide great isolation between OS components and at the expense of performance. Our work embraces both the monolithic design, aka no protection between components, and the modularity that micro kernels have advocated. With that, I'd like to give you a tour of the Unicraft internals, and how an application, given the third party libraries running on an operating system with a kernel, on a particular platform hardware, is componentized into a library operating system along with the Unicraft build system that takes individual components which are required for the application's runtime, and prepares it, fetches it, compiles it, links it, and provides a final Unicurnal image which can be run standalone on the target platform and hardware. Since all components are micro libraries that have their own makefile and kconfig configuration files, they can be added to the Unicurnal build independently of each other, unless of course micro libraries have a dependency on another, in which case the build system will target and build this dependency. APIs, such as POSIX sockets, are also micro libraries, which can be easily enabled or disabled via the kconfig menu. Unicurnals can thus compose which APIs to choose to best cater for the application's need, e.g. an RPC-style application might turn off the UK Schedule API in order to implement a high-performance run-to-completion event loop. I'd like to show you how easy it is to build Unicraft Unicurnals. You can use Unicraft's command line companion tool, Kraft, to download already supported micro libraries and applications, configure the Unicurnal to your needs, and build and run the Unicurnal itself. I have pre-installed Kraft on this Linux KVM host, and I've downloaded the source files via craftless pull. Here's an nginx application. The nginx application is configured via Kraft YAML. Here I can see which kconfig options are set, for which version of Unicraft's core, which architecture has been set to, which platform has been set to, and the libraries and dependencies that are required for it to build. I can open up the configuration menu by doing craftconfigure-k. Here I have a makemenu config. I can change the architecture platform and the individual library configurations. Here are all the different libraries, which have been enabled, and I can change individual things here. We can use Kraft build to compile the sources, which I've already done in advance. Once done, we get an output image inside of the build folder. Here is our Unicurnal. As you can see, there's only 1.7 megabytes in size. I can then run it using a pre-installed tool called Chemugest. This simply links to the kernel image, posited how much RAM I should put, which is a little bit too much, but hey-ho. Dash i for init-ramfs, which I've configured as the default file system. I'm attaching it to a network bridge. I'm setting its IP address statically, the address of the gateway statically, and it's subnet. Once run, it should just boot and start running. I can then do curl on the pre-allocated static IP address. So 1.7.2.99.02. And you'll see, I get a response. Arguably, an OS is only as good as the applications it can run, and this has been the thorn of Unicurnals since their inception, since they often require manual porting of applications. One approach is to provide binary compatibility, where unmodified binaries are taken and syscools are translated, at runtime, into a Unicurnal's underlying functionality. This approach has the advantage of requiring absolutely no porting work, but the translation comes with important penalties. So how does it compare? This table shows the results of microbenchmarks when compared with the cost of a no-op system and function calls with Unicraft and Linux. System calls are runtime translations in Unicraft are 2 to 8 times faster than in Linux, depending on whether the KPTI or other mitigations are enabled under Linux's host. However, system calls and runtime translations have a 10-fold performance cost compared to function calls, making binary compatibility pretty expensive. For virtual machines, running a single application, the syscool costs are likely not worth the cost at all, since isolation is also offered by the hypervisor. In this context, Unicurnals can get important performance benefits by removing the user kernel separation and its associated costs. The indirection required for binary compatibility reduces the Unicurnal benefits significantly. In order to avoid syscool penalty costs but minimize porting effort, we take a slightly different approach. We rely on the target application's native build system and use the statically compiled object files to link them into Unicraft's final linking step. We take the application build system, we compile it to.objectfiles and.a files, and then start linking it against, for instance, muscle. To support muscle, a libc-type library, which depends on the availability of Linux syscools, we create a syscool-shin layer, each library that implements a system call and registers with it via macro. The shim layer is then able to generate a system call interface at the libc level. In this way, we can link the system call implementations directly when compiling application sources natively with Unicraft, with the result that syscools are transformed into inexpensive function calls. Results from automated porting based on externally built archives when linking against Unicraft using muscle newlib, we show whether the port succeeded with the glibc compatibility layer, the compad layer, and without it the standard layer. As you can see, the approach is not effective with newlib. But it is with muscle. Most libraries build fully automatically. For those that do not, the reason has to do with the fact that glibc-specific symbols. To address this, we build a glibc compatibility layer based on a series of muscle patches and 20 other functions that we implement by hand, mostly 64-bit versions of file operations, such as pread and pwrite. So how many syscools are needed to get an application to run? Well, a study of modern Linux API users compatibility showed that around 50% of applications only needed about 145 syscools. Unicraft is nearly there. We're at 140. To show what Unicraft could transparently support, we use the Debian popularity contest to select a set of 30 popular server applications not yet supported by Unicraft, such as Apache, MongoDB, Postgres. To derive an accurate set of syscools, these applications required to actually run, we created a small framework consisting of various configurations, such as different port numbers for web servers, background mode, etc., and unit tests, so SQL queries for database servers, DNS queries, and DNS servers. These configurations and unit tests are then given as an input to the analyzer, which monitors the application's behavior by relying on the S Trace Utility Program. Once the dynamic analysis is done, the results are compared and added in the form of a static analysis. Here you can see a heat map, where we show the amount of individual syscools that are required by these 30 popular applications. When transparent support isn't available, there's also manual porting. And over time, we've been seeing that manual porting efforts have become smaller and smaller and smaller. With a short survey that we did from developers of Unicraft applications, we saw that the time taken to provide an application from start to finish with the Unicraft framework has become smaller. This is largely in fact due to the libraries provided by Unicraft have increased, making it easier to port applications individually. We've got quite a few libraries now. We can support modern languages, naturally C++, but there's other languages such as Go, Python, WebAssembly, and then modern applications such as Redis and Nginx. We also support some domain-specific frameworks such as DPDK and TensorFlow Lite. And we have ongoing work for Rust, OpenJDK's Java, and for JavaScript. With that, I'd like to start presenting some of the results we've done in the recent evaluation of Unicraft itself. Unicraft images are smaller than other Unicernal projects and Linux. Note that Linux sizes are just for the application and do not include the size of GLibc or the kernel itself. This is the consequence of Unicraft's modular approach, drastically reducing the amount of code for the compiled and linked application. For Hello World, no schedule or no memory allocated at all. Small image size not only useful for minimizing disk storage, but also to enable quick boot times for VMs based on those images. Here we've done an analysis of popular applications such as Redis, Nginx, and Mesculubit. Here we've measured boot time of Unicraft with different virtual machine monitors. We define boot time of a Unicernal instance as the time between the moment the virtualization tool stack, such as Camu, Firecracker, ZenZestal, etc., is invoked to respond to a VM boot to request, and the moment the Unicernal starts executing user code, i.e. the end of the guest OS boot process. This is essentially the time that a user would experience in a deployment, minus the application initialization time, which we leave out since it's independent from the Unicernal. And as you can see, Unicraft's boot time is pretty minimal across the board. Here we've measured the minimum amount of memory to run these popular applications. We define memory usage of a Unicernal instance as the resident set size, i.e. the amount of physical memory used on the guest Unicernal, plus the resident set sizes of the virtual tool stacks per VM data structure. This indicates how many instances would be able to run the host with a given amount of free RAM, which is ultimately what an operator or virtualization server cares about. Note that these measurements represent the upper bound of an instance memory usage. Some of the VMs' pages have potential for sharing across instances, e.g. shared libraries used by Camu, effectively reducing the per-VM resident set size when the VM density decreases. We use ports of Nginx and compare their performance to that of existing solutions, including the Linux VM and other Unicernals. Note that we did not optimize the applications for performance in these tests. We did, however, solve performance bottlenecks in Unicraft's networking stack implemented by Lightweight IP by adding memory pools to reduce packet allocation costs. These tests were run with 14 threads and 30 connections of a static payload of 612 bytes. We also measured the performance of Redis using Redis benchmark, using 30 connections and 100,000 requests at a pipelining level of 16. Here you can see that Unicraft is able to outperform the rest. Following these baseline performance evaluations, I'd like to show you how specializing Unicraft itself can further increase performance. Let's look at image size. We have motivated the importance of image size as a key performance indicator for small cloud images that are necessary to reduce Unicernal provisioning time and virtual machine startup time. Here we measured the image size of Unicernals. We built the Hello World, Escalite and Nginx Unicernals for KVM platform and varied a number of different configuration options, including dead code elimination, link time optimization and a combination of both. We performed two measurements, enabling and disabling link time optimization and dead code elimination. We compared the size of the resulting image using the DU utility in order to avoid incorrect measurements due to sparse images. We measured the performance of the Nginx web server in Unicraft with varying allocators. In order to limit the impact of IO calls and our results, logging was disabled. We used the Utility WRK, a HTTP benchmarking tool, and we benchmarked the server for one minute with 14 threads and 30 connections per host. Here you can see that Tani Alloc outperformed the rest of the allocators provided. Finally, we measured the internal boot time of Unicraft's Nginx Unicernal and found scalability issues when using the binary buddy memory allocator. Boot performance is similar with Escalite, with the buddy allocator being the worst and tiny alloc and TLSF among the best. At runtime, though, the pecking order depends on how many queries are run. Tiny Alloc is the fastest, for less than 1,000 queries, by 5-26%, becoming suboptimal with more requests, as its memory compaction algorithms are slower. Using meme alloc instead provides 20% performance boost when many queries are served. When we benchmarked Redis, further to confirm that no allocator is optimal for all workloads and the right choice of allocator for the workload and use case can boost performance by 2-fold. Let's take specialization even further with Unicraft. With Unicraft, we have the ability to further customize images in ways that are difficult to achieve even with existing solutions, be they monolithic OSs or other Unicernal projects. The highly modular nature of Unicraft libraries makes it possible to easily replace core components such as the memory allocator, as we've just seen, page table support, schedulers, etc. By default, however, a Unicraft binary contains an already initialized page table structure, which is loaded in memory by the virtual machine monitor. During boot, Unicraft simply enables paging and updates the page table base register to point to the right address. This is enough for most applications and provides fast boot performance. Let's see what happens when we change this for dynamic performance. Unicraft also has the dynamic page management support, which can be enabled when applications need to alter their virtual address space explicitly, for example via M-map. When this is used, the entire page table is populated at boot time. This figure shows that the guest with up to 32MB dynamic page table takes slightly longer to boot than one with a pre-initialized 1GB page table, and that if the boot time increases proportionally with the amount of memory. Let's look at trying to specialize the file system. Here we aim to obtain high performance with a web caching unicolonel and remove the default VFS layer in Unicraft and hook the application directly into a purpose-built, specialized, hash-based file system known as SHFS. To benchmark performance, we measure the time it takes to look up a file and open a file descriptor for it. For this purpose, we prepare a small root file system with files in the file system root. We measure the average time taken to do one request out of a loop of 1000 open requests and consider two cases. The open syscall requests where the file exists and the open request where the file does not exist. We compare the specialized setup versus running the same application in a Linux virtual machine with an init ram and files in the ram. And also versus the application running on Unicraft on top of a default configured, i.e. no specialized unicolonel. Okay, let's look at how we can specialize the networking stack. We implemented a UDP-based in-memory key value store using a standard receive message send message syscall and then created a lightweight-based IP Unicraft image on top of it, as well as for Linux binaries. We measured the request rate for each version and how long it can be sustained for, showing the results in this table. Unicraft's performance is 2.5 times better than the guest Linux image and half of that is out of bare metal Linux. Though performance is low across the board. To improve performance for Linux, we must amortize the cost of a system call, i.e. a single packet is sent per syscall in the most basic versions. To do this, we used batch versions of the message syscall, leading to a 2x improvement on both bare metal and guest cases. To further improve performance, we ported our app to run on top of dpdk, which requires a major overhaul of the app, so that it fits inside of the dpdk framework. This boosts the guest performance to 6.1 million requests per second, but the cost of using two cores of a VM, only exclusively for dpdk. For Unicraft, we removed the lightweight-based IP stack and scheduler altogether via the makemenu config and code against the netdev API, which we use in poly mode. Our specialized Unicurnal required similar porting effort to the dpdk one. Its performance matches the dpdk performance, but it does so using much less resources. This only needs one core instead of two. For dpdk, it has the image size of 1.4 megabytes compared to 1 gigabyte for linux image, and it boots in 80 milliseconds instead of a few seconds. Here we show the transmission throughput of Unicraft and linux kvm virtual machines with the same setup. As you can see, the throughput is high with lower packet sizes and decreases as packet size increases. For future directions and specialization, we want to introduce more combined mentalization, writing critical microlibraries in memory-safe, race-conditional, statically-typed verified languages such as Rust. We hope to use hardware-assisted memory separation such as Cherry or Intel MPKs, additional code reduction and sealing with the hypervisor so that set calls and read-only and execute-only is done after boot. And we hope to upstream standard features, address space layout randomization, stack protection, etc. And for those security-conscious minds, fuzzing for additional verification. You can find Unicraft online at our github.com. Or on our website, Unicraft.org. We also have mailing lists or you can follow us on Twitter at Unicraft SDK. That's it for this presentation. Thanks for listening.
 Hello, in this talk I'll be giving an update on the Linux Foundation Open Source Project  Unicraft, a fully modular and library sized Unicernel which aims to provide outstanding  performance while making it easy to port off-the-shelf applications into Unicernels.  My name is Alexander and I'm a PhD student at Lancaster University and I've been lucky  enough to join the project over the last year to help bring more applications, tools and  OS subsystems into the project as part of my research.  Let's set the scene.  Specialization is arguably the most effective way to achieve outstanding performance.  This is seen classically with hardware.  Most recently, with applications specific integrated circuits such as tensor processing  units for machine learning, Intel's Movidius, a vision processing unit which enables on-demand  computer AI vision, or FPGAs such as the NetFPGA for specialized line-rate network packet  processing.  But these solutions are costly, requiring additional overhead for the development, manufacturing  and installation.  Additionally, they are inherently scoped and can only perform a subset of compute by its  very nature, making them inextensible once built.  That's not to say that you can't reprogram them with new logic, but only defined by its  architecture.  When it comes to networking, performance can be achieved by crafting packets ready to go  as is shown by standstone, leveraging the ability to write to the wire at line-rate when the  traffic is known.  Another direction is to add the required OS functionality from scratch for each target  application, possibly by reusing code from existing operating systems.  This is the approach taken by Clickos to support click modular routers and with Minicash to  implement webcache, to just to name a few.  In the case of Clickos and Minicash, the resulting images are very lean, have great performance  and have small boot times.  The big problem is that the porting effort is huge, and that it has to be mostly repeated  for every single application or language.  MirageOS, Erlang on Zen and runtime.js are examples of unikernels that have language-specific  environments and have been created to provide performance optimizations for the language  itself.  So in the virtualization domain, unikernels are the golden standard for specialization,  showing impressive results in terms of image size, boot times, memory consumption and high  throughput.  Some of the other benefits that come from having a single memory address space that  will address later is the elimination of costly syscalls.  Many of the benefits of specialization towards performance is the result of being able to  hook the application at the right level of abstraction to extract the best performance,  and this can be done in two ways.  Transparenly, simply by compiling your application into a unikernel, you reap the benefits of  lower boot times and memory consumption, or to modify the application itself to hook  into these performance-oriented APIs.  For example, a web server aiming to serve millions of requests per second can access  a low-level, batch-based network API rather than going with a standard but slow socket.  Unikernels' networking subsystem aims to decouple the device driver side, such as  VertiOnet or NetFront, from the network stack or the low-level networking application.  So can you do it with Linux?  Well, existing monolithic OSes, such as Linux, have APIs for each component, but most APRs  are quite rich and they have evolved organically, and so component separation is often blurred  and aimed at achieving performance.  For example, send file short-circuits the networking stack and storage stacks.  We tried to quantify the API complexity in the Linux kernel and analyze dependencies  between the main components.  We used a program called Cscope to extract all the functions from all the sources of  the kernel components, and then for each call, check to see if the function is defined in  the same component or in a different one.  In the latter case, we recorded the dependency.  Here, you can see the dependencies between different subcomponents within Linux.  But doing it with existing Unikernels is still troublesome.  It still requires significant expert work to build and reuse existing OS components  to target the specific application.  Some Unikernel projects are not necessarily POSIX compliant, meaning that porting an application  is non-trivial, having to provide the same kind of functionality to the application itself.  And Unikernels, while smaller, are still monolithic, for example with Rump Run, that compiles against  the BSD kernel, still provides a lot of internal OS primitives that are not necessarily used  by the Unikernel itself.  So starting from an existing Unikernel project is suboptimal, since none of the options were  designed to support these inherent modularity and subsystem componentization.  We opted for a clean slate API design approach, though we did reuse components from existing  work when relevant.  All components including operating system primitives, drivers, platforms, code and libraries should  be easily added and removed when needed.  Even API should be modular.  In order to support existing or legacy applications and programming languages, we aim to provide  POSIX compliancy as optional components as themselves, while still allowing for specialization  under the API.  So the kernel should be fully modular, and the kernel should provide a number of performance-oriented  performance APIs.  In contrast to classical OS work, we can roughly split between monolithic kernels which have  great performance versus micro kernels that provide great isolation between OS components  and at the expense of performance.  Our work embraces both the monolithic design, aka no protection between components, and  the modularity that micro kernels have advocated.  With that, I'd like to give you a tour of the Unicraft internals, and how an application,  given the third party libraries running on an operating system with a kernel, on a particular  platform hardware, is componentized into a library operating system along with the Unicraft  build system that takes individual components which are required for the application's  runtime, and prepares it, fetches it, compiles it, links it, and provides a final Unicurnal  image which can be run standalone on the target platform and hardware.  Since all components are micro libraries that have their own makefile and kconfig configuration  files, they can be added to the Unicurnal build independently of each other, unless  of course micro libraries have a dependency on another, in which case the build system  will target and build this dependency.  APIs, such as POSIX sockets, are also micro libraries, which can be easily enabled or disabled  via the kconfig menu.  Unicurnals can thus compose which APIs to choose to best cater for the application's  need, e.g. an RPC-style application might turn off the UK Schedule API in order to implement  a high-performance run-to-completion event loop.  I'd like to show you how easy it is to build Unicraft Unicurnals.  You can use Unicraft's command line companion tool, Kraft, to download already supported  micro libraries and applications, configure the Unicurnal to your needs, and build and  run the Unicurnal itself.  I have pre-installed Kraft on this Linux KVM host, and I've downloaded the source files  via craftless pull.  Here's an nginx application.  The nginx application is configured via Kraft YAML.  Here I can see which kconfig options are set, for which version of Unicraft's core, which  architecture has been set to, which platform has been set to, and the libraries and dependencies  that are required for it to build.  I can open up the configuration menu by doing craftconfigure-k.  Here I have a makemenu config.  I can change the architecture platform and the individual library configurations.  Here are all the different libraries, which have been enabled, and I can change individual  things here.  We can use Kraft build to compile the sources, which I've already done in advance.  Once done, we get an output image inside of the build folder.  Here is our Unicurnal.  As you can see, there's only 1.7 megabytes in size.  I can then run it using a pre-installed tool called Chemugest.  This simply links to the kernel image, posited how much RAM I should put, which is a little  bit too much, but hey-ho.  Dash i for init-ramfs, which I've configured as the default file system.  I'm attaching it to a network bridge.  I'm setting its IP address statically, the address of the gateway statically, and it's  subnet.  Once run, it should just boot and start running.  I can then do curl on the pre-allocated static IP address.  So 1.7.2.99.02.  And you'll see, I get a response.  Arguably, an OS is only as good as the applications it can run, and this has been the thorn of  Unicurnals since their inception, since they often require manual porting of applications.  One approach is to provide binary compatibility, where unmodified binaries are taken and syscools  are translated, at runtime, into a Unicurnal's underlying functionality.  This approach has the advantage of requiring absolutely no porting work, but the translation  comes with important penalties.  So how does it compare?  This table shows the results of microbenchmarks when compared with the cost of a no-op system  and function calls with Unicraft and Linux.  System calls are runtime translations in Unicraft are 2 to 8 times faster than in Linux, depending  on whether the KPTI or other mitigations are enabled under Linux's host.  However, system calls and runtime translations have a 10-fold performance cost compared to  function calls, making binary compatibility pretty expensive.  For virtual machines, running a single application, the syscool costs are likely not worth the  cost at all, since isolation is also offered by the hypervisor.  In this context, Unicurnals can get important performance benefits by removing the user  kernel separation and its associated costs.  The indirection required for binary compatibility reduces the Unicurnal benefits significantly.  In order to avoid syscool penalty costs but minimize porting effort, we take a slightly  different approach.  We rely on the target application's native build system and use the statically compiled  object files to link them into Unicraft's final linking step.  We take the application build system, we compile it to.objectfiles and.a files, and then  start linking it against, for instance, muscle.  To support muscle, a libc-type library, which depends on the availability of Linux syscools,  we create a syscool-shin layer, each library that implements a system call and registers  with it via macro.  The shim layer is then able to generate a system call interface at the libc level.  In this way, we can link the system call implementations directly when compiling application sources  natively with Unicraft, with the result that syscools are transformed into inexpensive function  calls.  Results from automated porting based on externally built archives when linking against Unicraft  using muscle newlib, we show whether the port succeeded with the glibc compatibility layer,  the compad layer, and without it the standard layer.  As you can see, the approach is not effective with newlib.  But it is with muscle.  Most libraries build fully automatically.  For those that do not, the reason has to do with the fact that glibc-specific symbols.  To address this, we build a glibc compatibility layer based on a series of muscle patches  and 20 other functions that we implement by hand, mostly 64-bit versions of file operations,  such as pread and pwrite.  So how many syscools are needed to get an application to run?  Well, a study of modern Linux API users compatibility showed that around 50% of applications only  needed about 145 syscools.  Unicraft is nearly there.  We're at 140.  To show what Unicraft could transparently support, we use the Debian popularity contest  to select a set of 30 popular server applications not yet supported by Unicraft, such as Apache,  MongoDB, Postgres.  To derive an accurate set of syscools, these applications required to actually run, we  created a small framework consisting of various configurations, such as different port numbers  for web servers, background mode, etc., and unit tests, so SQL queries for database servers,  DNS queries, and DNS servers.  These configurations and unit tests are then given as an input to the analyzer, which monitors  the application's behavior by relying on the S Trace Utility Program.  Once the dynamic analysis is done, the results are compared and added in the form of a static  analysis.  Here you can see a heat map, where we show the amount of individual syscools that are  required by these 30 popular applications.  When transparent support isn't available, there's also manual porting.  And over time, we've been seeing that manual porting efforts have become smaller and smaller  and smaller.  With a short survey that we did from developers of Unicraft applications, we saw that the  time taken to provide an application from start to finish with the Unicraft framework  has become smaller.  This is largely in fact due to the libraries provided by Unicraft have increased, making  it easier to port applications individually.  We've got quite a few libraries now.  We can support modern languages, naturally C++, but there's other languages such as  Go, Python, WebAssembly, and then modern applications such as Redis and Nginx.  We also support some domain-specific frameworks such as DPDK and TensorFlow Lite.  And we have ongoing work for Rust, OpenJDK's Java, and for JavaScript.  With that, I'd like to start presenting some of the results we've done in the recent evaluation  of Unicraft itself.  Unicraft images are smaller than other Unicernal projects and Linux.  Note that Linux sizes are just for the application and do not include the size of GLibc or the  kernel itself.  This is the consequence of Unicraft's modular approach, drastically reducing the amount  of code for the compiled and linked application.  For Hello World, no schedule or no memory allocated at all.  Small image size not only useful for minimizing disk storage, but also to enable quick boot  times for VMs based on those images.  Here we've done an analysis of popular applications such as Redis, Nginx, and Mesculubit.  Here we've measured boot time of Unicraft with different virtual machine monitors.  We define boot time of a Unicernal instance as the time between the moment the virtualization  tool stack, such as Camu, Firecracker, ZenZestal, etc., is invoked to respond to a VM boot  to request, and the moment the Unicernal starts executing user code, i.e. the end of the guest  OS boot process.  This is essentially the time that a user would experience in a deployment, minus the application  initialization time, which we leave out since it's independent from the Unicernal.  And as you can see, Unicraft's boot time is pretty minimal across the board.  Here we've measured the minimum amount of memory to run these popular applications.  We define memory usage of a Unicernal instance as the resident set size, i.e. the amount  of physical memory used on the guest Unicernal, plus the resident set sizes of the virtual  tool stacks per VM data structure.  This indicates how many instances would be able to run the host with a given amount of  free RAM, which is ultimately what an operator or virtualization server cares about.  Note that these measurements represent the upper bound of an instance memory usage.  Some of the VMs' pages have potential for sharing across instances, e.g. shared libraries  used by Camu, effectively reducing the per-VM resident set size when the VM density decreases.  We use ports of Nginx and compare their performance to that of existing solutions, including the  Linux VM and other Unicernals.  Note that we did not optimize the applications for performance in these tests.  We did, however, solve performance bottlenecks in Unicraft's networking stack implemented  by Lightweight IP by adding memory pools to reduce packet allocation costs.  These tests were run with 14 threads and 30 connections of a static payload of 612 bytes.  We also measured the performance of Redis using Redis benchmark, using 30 connections  and 100,000 requests at a pipelining level of 16.  Here you can see that Unicraft is able to outperform the rest.  Following these baseline performance evaluations, I'd like to show you how specializing Unicraft  itself can further increase performance.  Let's look at image size.  We have motivated the importance of image size as a key performance indicator for small  cloud images that are necessary to reduce Unicernal provisioning time and virtual machine  startup time.  Here we measured the image size of Unicernals.  We built the Hello World, Escalite and Nginx Unicernals for KVM platform and varied a  number of different configuration options, including dead code elimination, link time  optimization and a combination of both.  We performed two measurements, enabling and disabling link time optimization and dead  code elimination.  We compared the size of the resulting image using the DU utility in order to avoid incorrect  measurements due to sparse images.  We measured the performance of the Nginx web server in Unicraft with varying allocators.  In order to limit the impact of IO calls and our results, logging was disabled.  We used the Utility WRK, a HTTP benchmarking tool, and we benchmarked the server for one  minute with 14 threads and 30 connections per host.  Here you can see that Tani Alloc outperformed the rest of the allocators provided.  Finally, we measured the internal boot time of Unicraft's Nginx Unicernal and found scalability  issues when using the binary buddy memory allocator.  Boot performance is similar with Escalite, with the buddy allocator being the worst and  tiny alloc and TLSF among the best.  At runtime, though, the pecking order depends on how many queries are run.  Tiny Alloc is the fastest, for less than 1,000 queries, by 5-26%, becoming suboptimal  with more requests, as its memory compaction algorithms are slower.  Using meme alloc instead provides 20% performance boost when many queries are served.  When we benchmarked Redis, further to confirm that no allocator is optimal for all workloads  and the right choice of allocator for the workload and use case can boost performance  by 2-fold.  Let's take specialization even further with Unicraft.  With Unicraft, we have the ability to further customize images in ways that are difficult  to achieve even with existing solutions, be they monolithic OSs or other Unicernal projects.  The highly modular nature of Unicraft libraries makes it possible to easily replace core components  such as the memory allocator, as we've just seen, page table support, schedulers, etc.  By default, however, a Unicraft binary contains an already initialized page table structure,  which is loaded in memory by the virtual machine monitor.  During boot, Unicraft simply enables paging and updates the page table base register to  point to the right address.  This is enough for most applications and provides fast boot performance.  Let's see what happens when we change this for dynamic performance.  Unicraft also has the dynamic page management support, which can be enabled when applications  need to alter their virtual address space explicitly, for example via M-map.  When this is used, the entire page table is populated at boot time.  This figure shows that the guest with up to 32MB dynamic page table takes slightly longer  to boot than one with a pre-initialized 1GB page table, and that if the boot time increases  proportionally with the amount of memory.  Let's look at trying to specialize the file system.  Here we aim to obtain high performance with a web caching unicolonel and remove the default  VFS layer in Unicraft and hook the application directly into a purpose-built, specialized,  hash-based file system known as SHFS.  To benchmark performance, we measure the time it takes to look up a file and open a file  descriptor for it.  For this purpose, we prepare a small root file system with files in the file system  root.  We measure the average time taken to do one request out of a loop of 1000 open requests  and consider two cases.  The open syscall requests where the file exists and the open request where the file does not  exist.  We compare the specialized setup versus running the same application in a Linux virtual machine  with an init ram and files in the ram.  And also versus the application running on Unicraft on top of a default configured, i.e.  no specialized unicolonel.  Okay, let's look at how we can specialize the networking stack.  We implemented a UDP-based in-memory key value store using a standard receive message send  message syscall and then created a lightweight-based IP Unicraft image on top of it, as well as  for Linux binaries.  We measured the request rate for each version and how long it can be sustained for, showing  the results in this table.  Unicraft's performance is 2.5 times better than the guest Linux image and half of that  is out of bare metal Linux.  Though performance is low across the board.  To improve performance for Linux, we must amortize the cost of a system call, i.e. a single  packet is sent per syscall in the most basic versions.  To do this, we used batch versions of the message syscall, leading to a 2x improvement  on both bare metal and guest cases.  To further improve performance, we ported our app to run on top of dpdk, which requires  a major overhaul of the app, so that it fits inside of the dpdk framework.  This boosts the guest performance to 6.1 million requests per second, but the cost of using  two cores of a VM, only exclusively for dpdk.  For Unicraft, we removed the lightweight-based IP stack and scheduler altogether via the  makemenu config and code against the netdev API, which we use in poly mode.  Our specialized Unicurnal required similar porting effort to the dpdk one.  Its performance matches the dpdk performance, but it does so using much less resources.  This only needs one core instead of two.  For dpdk, it has the image size of 1.4 megabytes compared to 1 gigabyte for linux image, and  it boots in 80 milliseconds instead of a few seconds.  Here we show the transmission throughput of Unicraft and linux kvm virtual machines with  the same setup.  As you can see, the throughput is high with lower packet sizes and decreases as packet  size increases.  For future directions and specialization, we want to introduce more combined mentalization,  writing critical microlibraries in memory-safe, race-conditional, statically-typed verified  languages such as Rust.  We hope to use hardware-assisted memory separation such as Cherry or Intel MPKs, additional code  reduction and sealing with the hypervisor so that set calls and read-only and execute-only  is done after boot.  And we hope to upstream standard features, address space layout randomization, stack protection,  etc.  And for those security-conscious minds, fuzzing for additional verification.  You can find Unicraft online at our github.com.  Or on our website, Unicraft.org.  We also have mailing lists or you can follow us on Twitter at Unicraft SDK.  That's it for this presentation.  Thanks for listening.
microkernel_unikraft_0_179.png microkernel_unikraft_180_3011.png microkernel_unikraft_3012_3479.png microkernel_unikraft_3480_4043.png microkernel_unikraft_4044_4307.png microkernel_unikraft_4308_5627.png microkernel_unikraft_5628_7559.png microkernel_unikraft_7560_8711.png microkernel_unikraft_8712_9959.png microkernel_unikraft_9960_10487.png microkernel_unikraft_10488_10799.png microkernel_unikraft_10800_11519.png microkernel_unikraft_11520_12215.png microkernel_unikraft_12216_12551.png microkernel_unikraft_12552_13271.png microkernel_unikraft_13272_16811.png microkernel_unikraft_16812_17219.png microkernel_unikraft_17220_18467.png microkernel_unikraft_18468_21539.png microkernel_unikraft_21540_23663.png microkernel_unikraft_23664_24839.png microkernel_unikraft_24840_25391.png microkernel_unikraft_25392_25655.png microkernel_unikraft_25656_26303.png microkernel_unikraft_26304_26627.png microkernel_unikraft_26628_27659.png microkernel_unikraft_27660_29687.png microkernel_unikraft_29688_29795.png microkernel_unikraft_29796_32423.png microkernel_unikraft_32424_33623.png microkernel_unikraft_33624_34122.png
Hello, in this talk I'll be giving an update on the Linux Foundation Open Source Project Unicraft, a fully modular and library sized Unicernel which aims to provide outstanding performance while making it easy to port off-the-shelf applications into Unicernels. My name is Alexander and I'm a PhD student at Lancaster University and I've been lucky enough to join the project over the last year to help bring more applications, tools and OS subsystems into the project as part of my research. Let's set the scene. Specialization is arguably the most effective way to achieve outstanding performance. This is seen classically with hardware. Most recently, with applications specific integrated circuits such as tensor processing units for machine learning, Intel's Movidius, a vision processing unit which enables on-demand computer AI vision, or FPGAs such as the NetFPGA for specialized line-rate network packet processing. But these solutions are costly, requiring additional overhead for the development, manufacturing and installation. Additionally, they are inherently scoped and can only perform a subset of compute by its very nature, making them inextensible once built. That's not to say that you can't reprogram them with new logic, but only defined by its architecture. When it comes to networking, performance can be achieved by crafting packets ready to go as is shown by standstone, leveraging the ability to write to the wire at line-rate when the traffic is known. Another direction is to add the required OS functionality from scratch for each target application, possibly by reusing code from existing operating systems. This is the approach taken by Clickos to support click modular routers and with Minicash to implement webcache, to just to name a few. In the case of Clickos and Minicash, the resulting images are very lean, have great performance and have small boot times. The big problem is that the porting effort is huge, and that it has to be mostly repeated for every single application or language. MirageOS, Erlang on Zen and runtime.js are examples of unikernels that have language-specific environments and have been created to provide performance optimizations for the language itself. So in the virtualization domain, unikernels are the golden standard for specialization, showing impressive results in terms of image size, boot times, memory consumption and high throughput. Some of the other benefits that come from having a single memory address space that will address later is the elimination of costly syscalls. Many of the benefits of specialization towards performance is the result of being able to hook the application at the right level of abstraction to extract the best performance, and this can be done in two ways. Transparenly, simply by compiling your application into a unikernel, you reap the benefits of lower boot times and memory consumption, or to modify the application itself to hook into these performance-oriented APIs. For example, a web server aiming to serve millions of requests per second can access a low-level, batch-based network API rather than going with a standard but slow socket. Unikernels' networking subsystem aims to decouple the device driver side, such as VertiOnet or NetFront, from the network stack or the low-level networking application. So can you do it with Linux? Well, existing monolithic OSes, such as Linux, have APIs for each component, but most APRs are quite rich and they have evolved organically, and so component separation is often blurred and aimed at achieving performance. For example, send file short-circuits the networking stack and storage stacks. We tried to quantify the API complexity in the Linux kernel and analyze dependencies between the main components. We used a program called Cscope to extract all the functions from all the sources of the kernel components, and then for each call, check to see if the function is defined in the same component or in a different one. In the latter case, we recorded the dependency. Here, you can see the dependencies between different subcomponents within Linux. But doing it with existing Unikernels is still troublesome. It still requires significant expert work to build and reuse existing OS components to target the specific application. Some Unikernel projects are not necessarily POSIX compliant, meaning that porting an application is non-trivial, having to provide the same kind of functionality to the application itself. And Unikernels, while smaller, are still monolithic, for example with Rump Run, that compiles against the BSD kernel, still provides a lot of internal OS primitives that are not necessarily used by the Unikernel itself. So starting from an existing Unikernel project is suboptimal, since none of the options were designed to support these inherent modularity and subsystem componentization. We opted for a clean slate API design approach, though we did reuse components from existing work when relevant. All components including operating system primitives, drivers, platforms, code and libraries should be easily added and removed when needed. Even API should be modular. In order to support existing or legacy applications and programming languages, we aim to provide POSIX compliancy as optional components as themselves, while still allowing for specialization under the API. So the kernel should be fully modular, and the kernel should provide a number of performance-oriented performance APIs. In contrast to classical OS work, we can roughly split between monolithic kernels which have great performance versus micro kernels that provide great isolation between OS components and at the expense of performance. Our work embraces both the monolithic design, aka no protection between components, and the modularity that micro kernels have advocated. With that, I'd like to give you a tour of the Unicraft internals, and how an application, given the third party libraries running on an operating system with a kernel, on a particular platform hardware, is componentized into a library operating system along with the Unicraft build system that takes individual components which are required for the application's runtime, and prepares it, fetches it, compiles it, links it, and provides a final Unicurnal image which can be run standalone on the target platform and hardware. Since all components are micro libraries that have their own makefile and kconfig configuration files, they can be added to the Unicurnal build independently of each other, unless of course micro libraries have a dependency on another, in which case the build system will target and build this dependency. APIs, such as POSIX sockets, are also micro libraries, which can be easily enabled or disabled via the kconfig menu. Unicurnals can thus compose which APIs to choose to best cater for the application's need, e.g. an RPC-style application might turn off the UK Schedule API in order to implement a high-performance run-to-completion event loop. I'd like to show you how easy it is to build Unicraft Unicurnals. You can use Unicraft's command line companion tool, Kraft, to download already supported micro libraries and applications, configure the Unicurnal to your needs, and build and run the Unicurnal itself. I have pre-installed Kraft on this Linux KVM host, and I've downloaded the source files via craftless pull. Here's an nginx application. The nginx application is configured via Kraft YAML. Here I can see which kconfig options are set, for which version of Unicraft's core, which architecture has been set to, which platform has been set to, and the libraries and dependencies that are required for it to build. I can open up the configuration menu by doing craftconfigure-k. Here I have a makemenu config. I can change the architecture platform and the individual library configurations. Here are all the different libraries, which have been enabled, and I can change individual things here. We can use Kraft build to compile the sources, which I've already done in advance. Once done, we get an output image inside of the build folder. Here is our Unicurnal. As you can see, there's only 1.7 megabytes in size. I can then run it using a pre-installed tool called Chemugest. This simply links to the kernel image, posited how much RAM I should put, which is a little bit too much, but hey-ho. Dash i for init-ramfs, which I've configured as the default file system. I'm attaching it to a network bridge. I'm setting its IP address statically, the address of the gateway statically, and it's subnet. Once run, it should just boot and start running. I can then do curl on the pre-allocated static IP address. So 1.7.2.99.02. And you'll see, I get a response. Arguably, an OS is only as good as the applications it can run, and this has been the thorn of Unicurnals since their inception, since they often require manual porting of applications. One approach is to provide binary compatibility, where unmodified binaries are taken and syscools are translated, at runtime, into a Unicurnal's underlying functionality. This approach has the advantage of requiring absolutely no porting work, but the translation comes with important penalties. So how does it compare? This table shows the results of microbenchmarks when compared with the cost of a no-op system and function calls with Unicraft and Linux. System calls are runtime translations in Unicraft are 2 to 8 times faster than in Linux, depending on whether the KPTI or other mitigations are enabled under Linux's host. However, system calls and runtime translations have a 10-fold performance cost compared to function calls, making binary compatibility pretty expensive. For virtual machines, running a single application, the syscool costs are likely not worth the cost at all, since isolation is also offered by the hypervisor. In this context, Unicurnals can get important performance benefits by removing the user kernel separation and its associated costs. The indirection required for binary compatibility reduces the Unicurnal benefits significantly. In order to avoid syscool penalty costs but minimize porting effort, we take a slightly different approach. We rely on the target application's native build system and use the statically compiled object files to link them into Unicraft's final linking step. We take the application build system, we compile it to.objectfiles and.a files, and then start linking it against, for instance, muscle. To support muscle, a libc-type library, which depends on the availability of Linux syscools, we create a syscool-shin layer, each library that implements a system call and registers with it via macro. The shim layer is then able to generate a system call interface at the libc level. In this way, we can link the system call implementations directly when compiling application sources natively with Unicraft, with the result that syscools are transformed into inexpensive function calls. Results from automated porting based on externally built archives when linking against Unicraft using muscle newlib, we show whether the port succeeded with the glibc compatibility layer, the compad layer, and without it the standard layer. As you can see, the approach is not effective with newlib. But it is with muscle. Most libraries build fully automatically. For those that do not, the reason has to do with the fact that glibc-specific symbols. To address this, we build a glibc compatibility layer based on a series of muscle patches and 20 other functions that we implement by hand, mostly 64-bit versions of file operations, such as pread and pwrite. So how many syscools are needed to get an application to run? Well, a study of modern Linux API users compatibility showed that around 50% of applications only needed about 145 syscools. Unicraft is nearly there. We're at 140. To show what Unicraft could transparently support, we use the Debian popularity contest to select a set of 30 popular server applications not yet supported by Unicraft, such as Apache, MongoDB, Postgres. To derive an accurate set of syscools, these applications required to actually run, we created a small framework consisting of various configurations, such as different port numbers for web servers, background mode, etc., and unit tests, so SQL queries for database servers, DNS queries, and DNS servers. These configurations and unit tests are then given as an input to the analyzer, which monitors the application's behavior by relying on the S Trace Utility Program. Once the dynamic analysis is done, the results are compared and added in the form of a static analysis. Here you can see a heat map, where we show the amount of individual syscools that are required by these 30 popular applications. When transparent support isn't available, there's also manual porting. And over time, we've been seeing that manual porting efforts have become smaller and smaller and smaller. With a short survey that we did from developers of Unicraft applications, we saw that the time taken to provide an application from start to finish with the Unicraft framework has become smaller. This is largely in fact due to the libraries provided by Unicraft have increased, making it easier to port applications individually. We've got quite a few libraries now. We can support modern languages, naturally C++, but there's other languages such as Go, Python, WebAssembly, and then modern applications such as Redis and Nginx. We also support some domain-specific frameworks such as DPDK and TensorFlow Lite. And we have ongoing work for Rust, OpenJDK's Java, and for JavaScript. With that, I'd like to start presenting some of the results we've done in the recent evaluation of Unicraft itself. Unicraft images are smaller than other Unicernal projects and Linux. Note that Linux sizes are just for the application and do not include the size of GLibc or the kernel itself. This is the consequence of Unicraft's modular approach, drastically reducing the amount of code for the compiled and linked application. For Hello World, no schedule or no memory allocated at all. Small image size not only useful for minimizing disk storage, but also to enable quick boot times for VMs based on those images. Here we've done an analysis of popular applications such as Redis, Nginx, and Mesculubit. Here we've measured boot time of Unicraft with different virtual machine monitors. We define boot time of a Unicernal instance as the time between the moment the virtualization tool stack, such as Camu, Firecracker, ZenZestal, etc., is invoked to respond to a VM boot to request, and the moment the Unicernal starts executing user code, i.e. the end of the guest OS boot process. This is essentially the time that a user would experience in a deployment, minus the application initialization time, which we leave out since it's independent from the Unicernal. And as you can see, Unicraft's boot time is pretty minimal across the board. Here we've measured the minimum amount of memory to run these popular applications. We define memory usage of a Unicernal instance as the resident set size, i.e. the amount of physical memory used on the guest Unicernal, plus the resident set sizes of the virtual tool stacks per VM data structure. This indicates how many instances would be able to run the host with a given amount of free RAM, which is ultimately what an operator or virtualization server cares about. Note that these measurements represent the upper bound of an instance memory usage. Some of the VMs' pages have potential for sharing across instances, e.g. shared libraries used by Camu, effectively reducing the per-VM resident set size when the VM density decreases. We use ports of Nginx and compare their performance to that of existing solutions, including the Linux VM and other Unicernals. Note that we did not optimize the applications for performance in these tests. We did, however, solve performance bottlenecks in Unicraft's networking stack implemented by Lightweight IP by adding memory pools to reduce packet allocation costs. These tests were run with 14 threads and 30 connections of a static payload of 612 bytes. We also measured the performance of Redis using Redis benchmark, using 30 connections and 100,000 requests at a pipelining level of 16. Here you can see that Unicraft is able to outperform the rest. Following these baseline performance evaluations, I'd like to show you how specializing Unicraft itself can further increase performance. Let's look at image size. We have motivated the importance of image size as a key performance indicator for small cloud images that are necessary to reduce Unicernal provisioning time and virtual machine startup time. Here we measured the image size of Unicernals. We built the Hello World, Escalite and Nginx Unicernals for KVM platform and varied a number of different configuration options, including dead code elimination, link time optimization and a combination of both. We performed two measurements, enabling and disabling link time optimization and dead code elimination. We compared the size of the resulting image using the DU utility in order to avoid incorrect measurements due to sparse images. We measured the performance of the Nginx web server in Unicraft with varying allocators. In order to limit the impact of IO calls and our results, logging was disabled. We used the Utility WRK, a HTTP benchmarking tool, and we benchmarked the server for one minute with 14 threads and 30 connections per host. Here you can see that Tani Alloc outperformed the rest of the allocators provided. Finally, we measured the internal boot time of Unicraft's Nginx Unicernal and found scalability issues when using the binary buddy memory allocator. Boot performance is similar with Escalite, with the buddy allocator being the worst and tiny alloc and TLSF among the best. At runtime, though, the pecking order depends on how many queries are run. Tiny Alloc is the fastest, for less than 1,000 queries, by 5-26%, becoming suboptimal with more requests, as its memory compaction algorithms are slower. Using meme alloc instead provides 20% performance boost when many queries are served. When we benchmarked Redis, further to confirm that no allocator is optimal for all workloads and the right choice of allocator for the workload and use case can boost performance by 2-fold. Let's take specialization even further with Unicraft. With Unicraft, we have the ability to further customize images in ways that are difficult to achieve even with existing solutions, be they monolithic OSs or other Unicernal projects. The highly modular nature of Unicraft libraries makes it possible to easily replace core components such as the memory allocator, as we've just seen, page table support, schedulers, etc. By default, however, a Unicraft binary contains an already initialized page table structure, which is loaded in memory by the virtual machine monitor. During boot, Unicraft simply enables paging and updates the page table base register to point to the right address. This is enough for most applications and provides fast boot performance. Let's see what happens when we change this for dynamic performance. Unicraft also has the dynamic page management support, which can be enabled when applications need to alter their virtual address space explicitly, for example via M-map. When this is used, the entire page table is populated at boot time. This figure shows that the guest with up to 32MB dynamic page table takes slightly longer to boot than one with a pre-initialized 1GB page table, and that if the boot time increases proportionally with the amount of memory. Let's look at trying to specialize the file system. Here we aim to obtain high performance with a web caching unicolonel and remove the default VFS layer in Unicraft and hook the application directly into a purpose-built, specialized, hash-based file system known as SHFS. To benchmark performance, we measure the time it takes to look up a file and open a file descriptor for it. For this purpose, we prepare a small root file system with files in the file system root. We measure the average time taken to do one request out of a loop of 1000 open requests and consider two cases. The open syscall requests where the file exists and the open request where the file does not exist. We compare the specialized setup versus running the same application in a Linux virtual machine with an init ram and files in the ram. And also versus the application running on Unicraft on top of a default configured, i.e. no specialized unicolonel. Okay, let's look at how we can specialize the networking stack. We implemented a UDP-based in-memory key value store using a standard receive message send message syscall and then created a lightweight-based IP Unicraft image on top of it, as well as for Linux binaries. We measured the request rate for each version and how long it can be sustained for, showing the results in this table. Unicraft's performance is 2.5 times better than the guest Linux image and half of that is out of bare metal Linux. Though performance is low across the board. To improve performance for Linux, we must amortize the cost of a system call, i.e. a single packet is sent per syscall in the most basic versions. To do this, we used batch versions of the message syscall, leading to a 2x improvement on both bare metal and guest cases. To further improve performance, we ported our app to run on top of dpdk, which requires a major overhaul of the app, so that it fits inside of the dpdk framework. This boosts the guest performance to 6.1 million requests per second, but the cost of using two cores of a VM, only exclusively for dpdk. For Unicraft, we removed the lightweight-based IP stack and scheduler altogether via the makemenu config and code against the netdev API, which we use in poly mode. Our specialized Unicurnal required similar porting effort to the dpdk one. Its performance matches the dpdk performance, but it does so using much less resources. This only needs one core instead of two. For dpdk, it has the image size of 1.4 megabytes compared to 1 gigabyte for linux image, and it boots in 80 milliseconds instead of a few seconds. Here we show the transmission throughput of Unicraft and linux kvm virtual machines with the same setup. As you can see, the throughput is high with lower packet sizes and decreases as packet size increases. For future directions and specialization, we want to introduce more combined mentalization, writing critical microlibraries in memory-safe, race-conditional, statically-typed verified languages such as Rust. We hope to use hardware-assisted memory separation such as Cherry or Intel MPKs, additional code reduction and sealing with the hypervisor so that set calls and read-only and execute-only is done after boot. And we hope to upstream standard features, address space layout randomization, stack protection, etc. And for those security-conscious minds, fuzzing for additional verification. You can find Unicraft online at our github.com. Or on our website, Unicraft.org. We also have mailing lists or you can follow us on Twitter at Unicraft SDK. That's it for this presentation. Thanks for listening.
Hello, in this talk I'll be giving an update on the Linux Foundation Open Source Project Unicraft, a fully modular and library sized Unicernel which aims to provide outstanding performance while making it easy to port off-the-shelf applications into Unicernels. My are examples of unikernels that have to hook into these performance-oriented APIs. For still provides a lot of internal OS primitives components which are required for the you how easy it is to build Unicraft Unicurnals. compatibility showed that around 50% of the Debian popularity contest to select a set of domain-specific frameworks such as DPDK and results we've done in the recent evaluation of images. Here we've done an analysis of popular applications such as Redis, Nginx, and to run the host with a given amount of free RAM, Unicraft with varying allocators. In order to at boot time. This figure shows that the guest file system. Here we aim to obtain high the application running on Unicraft on top of a the app, so that it fits inside of the dpdk using much less resources. This only needs one size of 1.4 megabytes compared to 1 gigabyte for