3.1Kпросмотров
5 марта 2025 г.
Score: 3.4K
Последний пост из серии "как ускорить функцию task" 😁 ⬆️ Напоминаю, мы с вами задавались вопросом, сколько потребуется потоков, чтобы в 10 раз ускорить функцию task, где 5% кода выполняется под локом. Вычисленный с помощью закона Амдала ответ в 20 потоков был позже поставлен под сомнение практическими экспериментами. Сегодня ставим точку в этой серии постов. 🧱 Предлагаю немного переработать архитектуру данного нам кода: 1. Выделить один отдельный поток, который будет заниматься исключительно препроцессингом данных (функцией prepareData, которая требует исполнения под локом). 2. Выделить группу потоков, которая будет заниматься параллельной обработкой подготовленных данных. 💡 Какие у нас ожидания производительности от данного рефакторинга? 🔎 Вся функция task целиком требует 100ms на выполнение. Функция prepareData занимает 5% этого времени (5ms). Получается, что если поток будет последовательно заниматься только выполнением prepareData, то он сможет делать целых 200 исполнений за одну секунду. Кстати, лок теперь можно убирать, ведь код выполняется на одном потоке. 🧑🏽💻 Модифицируем код следующим образом:
class Test { private val lock = ReentrantLock() private val sequentialWorker = Executors.newSingleThreadExecutor() private val parallelWorkers = Executors.newFixedThreadPool(10) fun task() { sequentialWorker.submit { val data = prepareData() parallelWorkers.submit { process(data) } } } private fun prepareData(): Data { Thread.sleep(5L) Data(1) } private fun process(data: Data) { Thread.sleep(95L) } data class Data(val d: Int)
} 🚀 Здесь используется 11 потоков - один отдельный поток выполняет последовательную часть кода и передает подготовленные данные в группу из оставшихся 10 потоков, которые выполняют обработку подготовленных данных. Как и ожидалось, такой код выполняет около 100 операций в секунду. Более того, при увеличении группы потоков с 10 до 20 этот код выполняет около 200 операций в секунду, что является его максимальным возможным ускорением. ⚖️ Безусловно, данное решение не всегда применимо. Часто блокировки "раскиданы" по всему коду и мы не имеем возможности вынести их в отдельную группу потоков. Но в данном случае, когда препроцессинг всегда осуществляется в одном месте перед вычислениями, мы можем использовать такую архитектуру 🔥. #threads