fn test() {
let mut channels = Arc::new(Mutex::new(Vec::with_capacity(PAR)));
let mut joins = Vec::with_capacity(PAR);
for _ in 0..N / PAR {
for _ in 0..PAR {
let mut channels = Arc::clone(&channels);
joins.push(thread::spawn(move || {
get(channels.lock().unwrap());
}));
}
}
for j in joins {
j.join().unwrap();
}
}
#[inline(always)]
fn get(mut channels: MutexGuard<Vec<mpsc::Sender<i32>>>) -> i32 {
let (tx, rx) = mpsc::channel();
channels.push(tx);
if channels.len() == PAR {
exec(channels);
} else {
drop(channels); // drop гварда отпускает мьютекс
}
rx.recv().unwrap()
}
#[inline(always)]
fn exec(mut channels: MutexGuard<Vec<mpsc::Sender<i32>>>) {
let mut i = 0;
for c in channels.iter() {
i += 1;
c.send(1).unwrap();
}
println!("{}", i);
channels.clear();
// а здесь гвард дропнется сам
}
fn test() {
let (tx, rx) = mpsc::channel::<mpsc::Sender<i32>>();
let mut handles = Vec::with_capacity(N + 1);
handles.push(thread::spawn(move || exec(rx)));
for _ in 0..N {
let tx = tx.clone();
handles.push(thread::spawn(move || {
get(tx);
}))
}
drop(tx);
for handle in handles {
handle.join().unwrap();
}
}
fn get(sender: mpsc::Sender<mpsc::Sender<i32>>) -> i32 {
let (tx, rx) = mpsc::channel();
sender.send(tx).unwrap();
rx.recv().unwrap()
}
fn exec(receiver: mpsc::Receiver<mpsc::Sender<i32>>) {
let mut channels = Vec::with_capacity(PAR);
loop {
for _ in 0..PAR {
let Ok(tx) = receiver.recv() else {
return;
};
channels.push(tx);
}
let mut i = 0;
for c in channels.iter() {
i += 1;
c.send(1).unwrap();
}
println!("{}", i);
channels.clear();
}
}
Но особо это профита не дает, так как основной пожиратель перфоманса - switch context в ОС. Тысячи потоков делают только хуже. Запускать потоков сильно больше чем есть ядер - это вообще плохая идея. Просто ради интереса переписал еще раз на асинхронных каналах tokio:async fn test() {
let (tx, rx) = mpsc::unbounded_channel::<mpsc::UnboundedSender<i32>>();
let mut handles = Vec::with_capacity(N + 1);
handles.push(tokio::spawn(async move { exec(rx).await }));
for _ in 0..N {
let tx = tx.clone();
handles.push(tokio::spawn(async move {
get(tx).await;
}))
}
drop(tx);
for handle in handles {
handle.await.unwrap();
}
}
async fn get(sender: mpsc::UnboundedSender<mpsc::UnboundedSender<i32>>) -> i32 {
let (tx, mut rx) = mpsc::unbounded_channel();
sender.send(tx).unwrap();
rx.recv().await.unwrap()
}
async fn exec(mut receiver: mpsc::UnboundedReceiver<mpsc::UnboundedSender<i32>>) {
let mut channels = Vec::with_capacity(PAR);
loop {
for _ in 0..PAR {
let Some(tx) = receiver.recv().await else {
return;
};
channels.push(tx);
}
let mut i = 0;
for c in channels.iter() {
i += 1;
c.send(1).unwrap();
}
println!("{}", i);
channels.clear();
}
}
и запустил в многопоточном рантайме в дефолтной конфигурации (количество воркеров == количеству ядер), работает быстрее в 19 раз.$promise1 = $client->getAsync('http://www.example.com/foo1');
$promise2 = $client->getAsync('http://www.example.com/foo2');
$promise3 = $client->getAsync('http://www.example.com/foo3');
$promises = [$promise1, $promise2, $promise3];
$results = GuzzleHttp\Promise\settle($promises)->wait(); // тут все результаты
FROM rust:latest as build
WORKDIR /test-tcp
COPY ./Cargo.lock ./Cargo.lock
COPY ./Cargo.toml ./Cargo.toml
COPY ./src ./src
RUN cargo build --release
FROM debian:buster-slim
COPY --from=build /test-tcp/target/release/test-tcp /usr/src/test-tcp
CMD ["/usr/src/test-tcp"]
RUN USER=root cargo new --bin test-tcp
Как быть в этом случае? Создавать единую таблицу с кучей null или же несколько раздельных таблиц? Или делать таблицу для общих свойств и вспомогательные таблицы для дополнительных свойств? Может, есть некая общепринятая практика в этом случае?Нет чёткой общепринятой практики, потому что в разных случаях оптимальное решение может быть разное, в зависимости от постановки задачи.
1 - есть ли что-то быстрее, чем tokio
2 - правильно ли я использую tokio
3 - насколько хорошая с точки зрения производительности идея использовать Arc
4 - можно ли ускорить саму по себе структуру HashMap или только переписывать?
pub struct DashMap<K, V, S = RandomState> {
shift: usize,
shards: Box<[RwLock<HashMap<K, V, S>>]>,
hasher: S,
}
hashmap_no_capacity_format_key__3M
time: [1.4810 s 1.5362 s 1.5952 s]
hashmap_set_capacity_format_key__3M
time: [1.0688 s 1.0744 s 1.0804 s]
btree_format_key__3M time: [754.93 ms 843.10 ms 933.95 ms]
vec_set_apacity__3M time: [1.7122 ms 1.7309 ms 1.7655 ms]
dashmap_rayon_format_key__3M
time: [294.76 ms 303.70 ms 316.85 ms]
btree_known_key__3M time: [554.56 ms 556.18 ms 558.41 ms]
use std::{
collections::{BTreeMap, HashMap},
time::Instant,
};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn hashmap_no_capacity_format_key(n: usize) -> HashMap<String, usize> {
let mut map = HashMap::new();
for i in 0..n {
let key = format!("key_{i}");
map.insert(key, i);
}
map
}
fn hashmap_set_capacity_format_key(n: usize) -> HashMap<String, usize> {
let mut map = HashMap::with_capacity(n + 1);
for i in 0..n {
let key = format!("key_{i}");
map.insert(key, i);
}
map
}
fn btreemap_format_key(n: usize) -> BTreeMap<String, usize> {
let mut map = BTreeMap::new();
for i in 0..n {
let key = format!("key_{i}");
map.insert(key, i);
}
map
}
fn vec_set_capacity(n: usize) -> Vec<usize> {
let mut vector = Vec::with_capacity(n);
for i in 0..n {
vector.push(i);
}
vector
}
fn btreemap_known_key(keys: impl Iterator<Item = (String, usize)>) -> usize {
let mut map = BTreeMap::new();
for (k, v) in keys {
map.insert(k, v);
}
map.len()
}
fn dashmap_rayon_format_key(n: usize) -> dashmap::DashMap<String, usize> {
use rayon::prelude::*;
let map = dashmap::DashMap::with_capacity(n);
(0..n).into_par_iter().for_each(|i| {
let key = format!("key_{i}");
map.insert(key, i);
});
map
}
fn bench(c: &mut Criterion) {
c.bench_function("hashmap_no_capacity_format_key__3M", |b| {
b.iter(|| hashmap_no_capacity_format_key(black_box(3_000_000)))
});
c.bench_function("hashmap_set_capacity_format_key__3M", |b| {
b.iter(|| hashmap_set_capacity_format_key(black_box(3_000_000)))
});
c.bench_function("btree_format_key__3M", |b| {
b.iter(|| btreemap_format_key(black_box(3_000_000)))
});
c.bench_function("vec_set_apacity__3M", |b| {
b.iter(|| vec_set_capacity(black_box(3_000_000)))
});
c.bench_function("dashmap_rayon_format_key__3M", |b| {
b.iter(|| dashmap_rayon_format_key(black_box(3_000_000)))
});
c.bench_function("btree_known_key__3M", |b| {
b.iter_custom(|times| {
let mut total = vec![];
for _ in 0..times {
let mut keys = Vec::with_capacity(3_000_000);
for i in 0..3_000_000 {
keys.push((format!("key_{i}"), i));
}
let start = Instant::now();
black_box(btreemap_known_key(black_box(keys.drain(..))));
total.push(start.elapsed());
}
total.iter().sum()
});
});
}
criterion_group! {
name = benches;
config = Criterion::default().sample_size(10);
targets = bench
}
criterion_main!(benches);
.\rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain stable --profile default
RUSTUP_INIT_SKIP_EXISTENCE_CHECKS=yes
myItems.map(item => (
<a href={item.link}>{item.text}</a>
))
const images = {
red: smoke1Red,
blue: smoke1Blue,
brown: smoke1Brown,
}
...
<img src={images[color] || smoke1Gray} />