Blog

How to Speed Up Your Docker Container

Devs
Yauheni Dzirvuk
5min

Do you know that your code in the docker container can work as fast as on the host machine? In some cases, it can be even faster! 

Today, we’ll share some tips on optimizing the Docker settings that allowed us to decrease processing time by 15%. 

Testing environment setup

Before we begin, let’s go through the main environment features and tools that we’ll be using in our tests:

  • Operating system - Ubuntu 20.04
  • Benchmark tests - a script in Python (versions 3.8, 3.9, 3.10)
  • Environment - Docker v. 20.10.12

We’ll run CPU and I/O tests on the host machine and inside the container.

Why performance difference occurs

One of the benefits of containers over virtual machines is that you isolate processes without any performance losses or hypervisor corruption. However, Docker can slow down your code and skew efficiency indicators. And even if containers don’t slow down your code, there is a chance that it can run even faster with some optimization.

How is the difference in performance possible?

There is a theory that Docker's security features slow down computing. This is because Docker adds extra layers of security to prevent programs from escaping the container onto the host. One such security mechanism is seccomp. It defines what the system calls can run containers. It is important to note that this mechanism shouldn’t make such a difference, and there are suggestions on how to level the difference, but the corrections have not been applied yet.

To check if speedups are possible in your calculations quickly, you can run the container in privileged mode. It turns off the safety features, so you can determine if the slowdown is because of them. For this, run the container using the key —privileged:

        docker run -it --privileged <image_name>
    

 

Use the following parameter for docker-compose:

        services:
          agent:
            ...
            **privileged: true**
    

 

It is important to highlight that running containers in privileged mode can be unsafe. Here you can read why and how to deal with it. 

CPU tests

The first test we will run is computational operations testing in different container launch modes. As a basic benchmark, we will use the following computational operations:

        BENCHMARK="import timeit; print(timeit.timeit('list(range(10000))', number=50000))"
    

 

We will do each test five times and use the average value as an estimated result.

Let's do the first three tests:

  • Test on the host machine (running natively in the operating system):
        BENCHMARK="import timeit; print(timeit.timeit('list(range(10000))', number=50000))"
        python3.8 -c "$BENCHMARK"
    

 

  • Test in a container based on the ubuntu 20.04 image:
        docker run -it ubuntu:20.04
        BENCHMARK="import timeit; print(timeit.timeit('list(range(10000))', number=50000))"
        python3.8 -c "$BENCHMARK"
    

 

  • Test in a container based on the ubuntu 20.04 image with privileged mode turned on:
        docker run -it --privileged ubuntu:20.04
        BENCHMARK="import timeit; print(timeit.timeit('list(range(10000))', number=50000))"
        python3.8 -c "$BENCHMARK"
    

 

Here are the results we’ve got:

As you can see, inside the container, calculations are 13% longer but 4% faster than on the host machine when using the privileged mode. Parameter optimization showed the expected values. Moreover, in a container, calculations are even faster.

Testing of different images

Next, we will test various images. It may not be clear right away, but different images use different versions of services that can affect calculation performance. Let's run three tests using the following Python images:

  • Full official image container test - Python:3.8:
        BENCHMARK="import timeit; print(timeit.timeit('list(range(10000))', number=50000))"
        docker run python:3.8 python -c "$BENCHMARK"
    

 

  • Test in a container based on slim image - python:3.8-slim:
        BENCHMARK="import timeit; print(timeit.timeit('list(range(10000))', number=50000))"
        docker run python:3.8-slim python -c "$BENCHMARK"
    

 

  • Test in a container based on alpine image - python:3.8-alpine:
        BENCHMARK="import timeit; print(timeit.timeit('list(range(10000))', number=50000))"
        docker run python:3.8-alpine python -c "$BENCHMARK"
    

 

Here are the results we’ve got:

As you can see, calculations in the slim image are 2% faster than the official image and 20% faster than the alpine. Please note that this benchmark is faster than host machines on 25+% in all tests.

Also, we want to stress one more time that these tests are focused on calculations. In containers running in production, such operations are mixed with others, and there will not be such a big difference. You can learn more about the choice of image and the difference between them here.

Different versions of Python distributions testing 

Now, let’s compare the calculations of different versions of Python distributions. We'll run three tests and see what results we get. Here are the versions of Python we’ll use:

  • Test in a container based on slim image - python:3.8-slim:
        BENCHMARK="import timeit; print(timeit.timeit('list(range(10000))', number=50000))"
        docker run python:3.8-slim python -c "$BENCHMARK"
    

 

  • Test in a container based on slim image - python:3.9-slim:
        BENCHMARK="import timeit; print(timeit.timeit('list(range(10000))', number=50000))"
        docker run python:3.9-slim python -c "$BENCHMARK"
    

 

  • Test in a container based on slim image - python:3.10-slim:
        BENCHMARK="import timeit; print(timeit.timeit('list(range(10000))', number=50000))"
        docker run python:3.10-slim python -c "$BENCHMARK"
    

 

And here is a diagram with the results:

We’ve got quite unexpected results. The test performed on the python:3.10-slim image is 70% faster than the test on the host machine. This shows us another option for speeding up your applications in a container.

I/O tests

In addition to computational tests, it is of interest to run input/output tests on the host machine and in the container. It is expected that these tests will show differences in the results.

As a benchmark, we will use the script, which is a character-by-character read and write of a 128MB file.

Let's run four tests:

  • Test on the host machine (running natively in the operating system):
        python3.8 io_speed.py
    

 

  • Test in a container based on slim image - python:3.8-slim:
        docker run python:3.8-slim python -c "`cat io_speed.py`"
    

 

  • Test in a container based on slim image - python:3.8-slim with privileged mode:
        docker run --privileged python:3.8-slim python -c "`cat io_speed.py`"
    

 

  • Test in a container based on an ubuntu 20.04 image with privileged mode:
        docker run --privileged ubuntu:20.04 python -c "`cat io_speed.py`"
    

 

And here are the results we’ve got:

As you can see, the computations in the container are 40% slower. You can get similar results using an image based on Ubuntu 20.04 with privileged mode only. Note that in the tests mentioned before, this image proved to be worse than others.

Now, we hope you have a better understanding of why it is necessary to run tests and find your best configuration for each task.

Other tests

These are not all possible tests and reasons for poor performance. You can investigate network overhead issues more by yourself, as containers can double the latency. We will not consider other tests in detail because they often show a small difference.

Also, the use of volumes can add extra overhead. This is especially important for Windows and macOS users. On Linux, they incur no overhead since the underlying VFS is shared between the host and container directly. But you don't need to refuse to use volumes. You should understand that many images are designed to use them (for example, some images for the database).

Conclusion

Our today's results show that containers provide equal or better performance in almost all cases. And here are the main insights we wanted to share:

  • To quickly check if speedups are possible in your calculations, you can run the container in privileged mode. But be sure you know what you are doing since this is not the safest option.
  • In addition to the access mode, you should pay attention to container images and distribution versions. This allows you to speed up containers and make their calculations faster than on the host machine.
  • You need to run tests and find the best configurations for each task.

Finally, we want to note that the tuning of the applications that use GPU servers is another big topic. So if you want us to get it covered, let us know.

Read more on Docker speed optimization:

Six Ways to Build Docker Images Faster

Speed Up Your Development Flow With These Dockerfile Best Practices

An Updated Performance Comparison of Virtual Machines and Linux Containers