Skip to content

Commit

Permalink
Use os.sched_getaffinity to get info about available CPUs
Browse files Browse the repository at this point in the history
when running inside containers with CPU limits python's
multiprocessing.cpu_count() will return total number of CPU's
available on the machine. However in reality the process and its
children will not be able to be scheduled on all CPUs.
This issue is most pronounced with taskset/cpuset is set for the process.
Causing scheduling contention over limited number of CPU's.

In case of CFS cpu limits, its similar, but due to bursty nature, at least
in a short timeframe the processes will be able to be scheduled on all CPUs.

There is no straightforward way to read the CPU shares assigned via CFS.
So just handling simple case when affinity is explicitly set should be a good start
  • Loading branch information
pawelchcki committed May 27, 2021
1 parent 34caa75 commit 4cc644a
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 8 deletions.
8 changes: 4 additions & 4 deletions chia/consensus/blockchain.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import asyncio
import dataclasses
import logging
import multiprocessing
from concurrent.futures.process import ProcessPoolExecutor
from enum import Enum
from typing import Dict, List, Optional, Set, Tuple, Union
Expand Down Expand Up @@ -35,6 +34,8 @@
from chia.util.generator_tools import get_block_header, tx_removals_and_additions
from chia.util.ints import uint16, uint32, uint64, uint128
from chia.util.streamable import recurse_jsonify
from chia.util.cpus import get_available_cpus


log = logging.getLogger(__name__)

Expand Down Expand Up @@ -98,9 +99,8 @@ async def create(
self = Blockchain()
self.lock = asyncio.Lock() # External lock handled by full node
self.compact_proof_lock = asyncio.Lock()
cpu_count = multiprocessing.cpu_count()
if cpu_count > 61:
cpu_count = 61 # Windows Server 2016 has an issue https://bugs.python.org/issue26903
cpu_count = get_available_cpus()

num_workers = max(cpu_count - 2, 1)
self.pool = ProcessPoolExecutor(max_workers=num_workers)
log.info(f"Started {num_workers} processes for block validation")
Expand Down
16 changes: 16 additions & 0 deletions chia/util/cpus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os
import multiprocessing


def get_available_cpus() -> int:
try:
cpu_count = os.sched_getaffinity(0)
except AttributeError:
cpu_count = multiprocessing.cpu_count()

# Note: Windows Server 2016 has an issue https://bugs.python.org/issue26903
if os.name == "nt":
MAX = 61
cpu_count = min(cpu_count, MAX)

return cpu_count
7 changes: 3 additions & 4 deletions chia/wallet/wallet_blockchain.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import asyncio
import dataclasses
import logging
import multiprocessing
from concurrent.futures.process import ProcessPoolExecutor
from enum import Enum
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
Expand All @@ -21,6 +20,7 @@
from chia.util.errors import Err, ValidationError
from chia.util.ints import uint32, uint64
from chia.util.streamable import recurse_jsonify
from chia.util.cpus import get_available_cpus
from chia.wallet.block_record import HeaderBlockRecord
from chia.wallet.wallet_block_store import WalletBlockStore
from chia.wallet.wallet_coin_store import WalletCoinStore
Expand Down Expand Up @@ -95,9 +95,8 @@ async def create(
self.lock = asyncio.Lock()
self.coin_store = coin_store
self.tx_store = tx_store
cpu_count = multiprocessing.cpu_count()
if cpu_count > 61:
cpu_count = 61 # Windows Server 2016 has an issue https://bugs.python.org/issue26903
cpu_count = get_available_cpus()

num_workers = max(cpu_count - 2, 1)
self.pool = ProcessPoolExecutor(max_workers=num_workers)
log.info(f"Started {num_workers} processes for block validation")
Expand Down

0 comments on commit 4cc644a

Please sign in to comment.