|
| 1 | +.. Licensed to the Apache Software Foundation (ASF) under one |
| 2 | +.. or more contributor license agreements. See the NOTICE file |
| 3 | +.. distributed with this work for additional information |
| 4 | +.. regarding copyright ownership. The ASF licenses this file |
| 5 | +.. to you under the Apache License, Version 2.0 (the |
| 6 | +.. "License"); you may not use this file except in compliance |
| 7 | +.. with the License. You may obtain a copy of the License at |
| 8 | +
|
| 9 | +.. http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +
|
| 11 | +.. Unless required by applicable law or agreed to in writing, |
| 12 | +.. software distributed under the License is distributed on an |
| 13 | +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 14 | +.. KIND, either express or implied. See the License for the |
| 15 | +.. specific language governing permissions and limitations |
| 16 | +.. under the License. |
| 17 | +
|
| 18 | +.. default-domain:: cpp |
| 19 | +.. highlight:: cpp |
| 20 | + |
| 21 | +.. _cpp_thread_management: |
| 22 | + |
| 23 | +================= |
| 24 | +Thread Management |
| 25 | +================= |
| 26 | + |
| 27 | +.. seealso:: |
| 28 | + :doc:`Thread management API reference <api/thread>` |
| 29 | + |
| 30 | +Thread Pools |
| 31 | +============ |
| 32 | + |
| 33 | +Many Arrow C++ operations distribute work across multiple threads to take |
| 34 | +advantage of underlying hardware parallelism. For example, when :func:`reading a |
| 35 | +Parquet file<parquet::arrow::FileReader::set_use_threads>` we can decode each |
| 36 | +column in parallel. To achieve this we submit tasks to an executor of some kind. |
| 37 | + |
| 38 | +Within Arrow C++ we use thread pools for parallel scheduling and an event loop |
| 39 | +when the user has requested serial execution. It is possible for |
| 40 | +users to provide their own custom implementation, though that is an advanced |
| 41 | +concept and not covered here. |
| 42 | + |
| 43 | +CPU vs. I/O |
| 44 | +----------- |
| 45 | + |
| 46 | +In order to minimize the overhead of context switches our default thread pool |
| 47 | +for CPU-intensive tasks has a fixed size, defaulting to |
| 48 | +`std::thread::hardware_concurrency <https://en.cppreference.com/w/cpp/thread/thread/hardware_concurrency>`_. |
| 49 | +This means that CPU tasks should never block for long periods of time because this |
| 50 | +will result in under-utilization of the CPU. To achieve this we have a separate |
| 51 | +thread pool which should be used for tasks that need to block. Since these tasks |
| 52 | +are usually associated with I/O operations we call this the I/O thread pool. This |
| 53 | +model is often associated with asynchronous computation. |
| 54 | + |
| 55 | +.. _io_thread_pool: |
| 56 | + |
| 57 | +The size of the I/O thread pool currently defaults to 8 threads and should |
| 58 | +be sized according to the parallel capabilities of the I/O hardware. For example, |
| 59 | +if most reads and writes occur on a typical HDD then the default of 8 will probably |
| 60 | +be sufficient. On the other hand, when most reads and writes occur on a remote |
| 61 | +filesystem such as S3, it is often possible to benefit from many concurrent reads |
| 62 | +and it may be possible to increase I/O performance by increasing the size of the |
| 63 | +I/O thread pool. The size of the default I/O thread pool can be managed with |
| 64 | +the :envvar:`ARROW_IO_THREADS` environment variable or |
| 65 | +with the :func:`arrow::io::SetIOThreadPoolCapacity` function. |
| 66 | + |
| 67 | +Increasing the size of the CPU thread pool is not likely to have any benefit. In |
| 68 | +some cases it may make sense to decrease the size of the CPU thread pool in order |
| 69 | +to reduce the impact that Arrow C++ has on hardware shared with other processes or user |
| 70 | +threads. The size of the default CPU thread pool can be managed with the |
| 71 | +:envvar:`OMP_NUM_THREADS` environment variable or with the |
| 72 | +:func:`arrow::SetCpuThreadPoolCapacity` function. |
| 73 | + |
| 74 | +Serial Execution |
| 75 | +---------------- |
| 76 | + |
| 77 | +Operations in Arrow C++ that may use threads can usually be configured to run serially |
| 78 | +via some kind of parameter. In this case we typically replace the CPU executor with |
| 79 | +an event loop operated by the calling thread. However, many operations will continue |
| 80 | +to use the I/O thread pool. This means that some parallelism may still occur even when |
| 81 | +serial execution is requested. |
| 82 | + |
| 83 | +Jemalloc Background Threads |
| 84 | +--------------------------- |
| 85 | + |
| 86 | +When using the :ref:`jemalloc allocator<cpp_memory_pool>` a small number of |
| 87 | +background threads will be created by jemalloc to manage the pool. These threads |
| 88 | +should have minimal impact but can show up as a memory leak when running analysis |
| 89 | +tools like Valgrind. This is harmless and can be safely suppressed or Arrow C++ can |
| 90 | +be compiled without jemalloc. |
| 91 | + |
| 92 | +Asynchronous Utilities |
| 93 | +====================== |
| 94 | + |
| 95 | +Future |
| 96 | +------ |
| 97 | + |
| 98 | +Arrow C++ uses :class:`arrow::Future` to communicate results between threads. Typically |
| 99 | +an :class:`arrow::Future` will be created when an operation needs to perform some kind |
| 100 | +of long running task that will block for some period of time. :class:`arrow::Future` |
| 101 | +objects are mainly meant for internal use and any method that returns an |
| 102 | +:class:`arrow::Future` will usually have a synchronous variant as well. |
0 commit comments