hyperion/20-dev/00-rust/20-livecoding/sum_of_squares.md

2.8 KiB

use std::thread;

pub fn sum_squares_scope(data: &[i32]) -> i32 {
    let num_threads = 4;
    let chunk_size = data.len().div_ceil(num_threads); // (len + n - 1) / n

    // thread::scope гарантирует, что все потоки внутри него 
    // завершатся до закрывающей скобки '}'
    thread::scope(|s| {
        let mut handles = Vec::with_capacity(num_threads);

        for chunk in data.chunks(chunk_size) {
            // Мы передаем ссылку `chunk` (&[i32]) внутрь потока.
            // Это разрешено ТОЛЬКО внутри scope, потому что Rust знает:
            // данные (data) точно переживут эти потоки.
            handles.push(s.spawn(move || {
                chunk.iter().map(|&x| x * x).sum::<i32>()
            }));
        }

        // Собираем результаты
        handles.into_iter().map(|h| h.join().unwrap()).sum()
    })
}

use std::sync::Arc;
use std::thread;
use std::iter::Sum;
use std::ops::Mul;

// T: Copy + Mul<Output=T> + Sum + Send + Sync + 'static
// Copy - чтобы числа копировались (дешево)
// Mul - чтобы умножать
// Sum - чтобы складывать
// Send + Sync - чтобы передавать между потоками
// 'static - нужно для thread::spawn (если без scope)

pub fn sum_squares_generic<T>(data: Vec<T>) -> T 
where 
    T: Copy + Mul<Output = T> + Sum + Send + Sync + 'static
{
    let num_threads = thread::available_parallelism()
        .map(|n| n.get())
        .unwrap_or(1);
        
    // Zero-copy: превращаем Vec<T> в Arc<[T]>
    // Это дешево, не копирует данные (просто меняет метаданные аллокации)
    let data_arc: Arc<[T]> = Arc::from(data); 

    let chunk_size = data_arc.len().div_ceil(num_threads);
    let mut handles = Vec::with_capacity(num_threads);

    for i in 0..num_threads {
        let data_ref = Arc::clone(&data_arc);
        
        handles.push(thread::spawn(move || {
            let start = i * chunk_size;
            let end = std::cmp::min(start + chunk_size, data_ref.len());
			
            if start >= data_ref.len() { 
                // Нам нужно вернуть "ноль" (нейтральный элемент суммы).
                // Так как T - дженерик, мы не можем просто написать 0.
                // Хитрость: сумма пустого итератора дает ноль типа T.
                return std::iter::empty::<T>().sum(); 
            }

            data_ref[start..end].iter()
                .map(|&x| x * x)
                .sum()
        }));
    }

    handles.into_iter().map(|h| h.join().unwrap()).sum()
}