И снова здраствуйте, сейчас мне нужно как можно лучше оптимизировать(ускорить?) данный код:
for (index,value) in vector.iter().enumerate() {
if value == "1.0" {
do_something(index);
}
}
Сейчас я получаю такой результат по времени: 114 миллисикунд когда длинна вектора равна 14.Но размер вектора может быть в разы больше и хотелось бы узнать, как можно это все ускорить, и возможно ли это вообще?
Сейчас просто использую крейт time для того что бы засечь время со старта и конца вычислений, в основном результат именно 100-120 миллисекунд.
Параметры такие:
– CPU: Intel Core i5-4460 @ 4x 3.4GHz
– GPU: GeForce GTX 760
– RAM: 8GB
Да с релиз и еще немного погуглил и решил LTO в релизе включить.
Задача в том что бы определить есть ли нужное value в векторе и если есть то добавить его в hashmap, точнее его индекс
А ты уверен что у тебя проблема в for? и как он у тебя формируется?
У меня получились следующие цифры:
for test time: [30.951 ns 31.011 ns 31.119 ns]
change: [-98.483% -98.448% -98.410%] (p = 0.00 < 0.05)
Performance has improved.
Found 15 outliers among 100 measurements (15.00%)
3 (3.00%) low severe
1 (1.00%) high mild
11 (11.00%) high severe
Поэтому и задал вопрос как ускорить, сам не знаю почему так долго выполняется, да и тот код привел как пример, у меня там немного по другому, там tuple Enum матчится
А что метод do_something делает? мне кажется в нем у тебя проблема. For в любом случае работать должен быстро, проблема либо в итерации(ты на каждой итерации что то сложное делаешь), либо замеры не правильно делаешь. попробуй все таки протестировать библиотекой которую выше привели.
Во-первых, надо выкладывать полный код бенчей.
Во-вторых, бенч функции test_for не имеет смысла, т.к. внутри нее идет создание vec!, что есть затратная операция, ибо приходится аллоцировать память в хипе. Функция, которую ты бенчишь, должна быть максимально короткой и делать только то, что ты бенчишь. Т.е. она должна по ссылке принимать уже сформированный массив (ну или слайс), и возвращать result: usize.
В-третьих, ты должен сказать нам порядок размера твоего массива, для которого ты пишешь эту функцию. Если это… 1000 элементов и вызываешь эту функцию раз в секунду, забей на оптимизацию. Если это 1М элементов и будешь вызывать каждую миллисекунду, то это уже хороший кандидат на оптимизацию.
В-четвертых, почему это массив строк, да которые еще и выглядят как флоаты?
В-пятых, будут ли флоаты отсортированы? Каков порядок элементов?
3 пункт очень важен. Если у тебя там 100 элементов, то не стоит тратить на эту “оптимизацию” время.
Это плохой бенч. Смотри в сторону стандартного бенчмаркинга или Criterion. Следует прогревать кеши перед замерами, замерять не 1 итерацию, а несколько итераций, с одними и теми же аргументами. Смысла в println! внутри бенчмарка нет. Инициализация вектора сделана неправильно, let v: Vec<String> = (0..n).map(|n| format!("{}.0", n + 1)).collect(); - это означает, что в нем только 1 элемент, а надо протестировать отсутствие элемента, когда все элементы - “1.0”… и т.д. Ну и дождаться от автора поста ответов на уже предоставленную информацию.
match value {
Operation::MakeLabel(label_id) => {
labeled.insert(label_id, index);
},
_operation => {}
};
@kpp дело в том что вектор может быть размером от нуля до нескольких тысяч, хотя возможно и не стоит сильно беспокоиться о такой скорости в 0.1c ибо эта программа просто для себя, но все же хотелось бы что бы все было побыстрее
На основе того что было выше накидал дуболомный вариант с HashMap, который и вектор внутри себя выделяет, и HashMap выделяет, и еще строчку клонирует при сохранении:
$ cat Cargo.toml:
[package]
name = "criterion_test"
version = "0.1.0"
authors = ["Andrey Lesnikov <ozkriff@gmail.com>"]
edition = "2018"
[dependencies]
[dev-dependencies]
criterion = "0.2"
[[bench]]
name = "my_benchmark"
harness = false
$ cat benches/my_benchmark.rs:
#[macro_use]
extern crate criterion;
use std::collections::HashMap;
use criterion::Criterion;
fn f() -> u64 {
let vector = vec![
"1.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0", "10.0", "11.0", "12.0",
"13.0", "14.0",
];
let mut map: HashMap<usize, String> = HashMap::new();
for (index, value) in vector.iter().enumerate() {
if value == &"1.0" {
map.insert(index, value.to_string());
}
}
map.len() as _
}
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("fib 20", |b| b.iter(|| f()));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
$ cargo bench
Finished release [optimized] target(s) in 0.09s
Running target/release/deps/criterion_test-b6d4e6c476bc742b
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Running target/release/deps/my_benchmark-a2c6c11db5814243
Gnuplot not found, disabling plotting
fib 20 time: [222.39 ns 226.60 ns 231.63 ns]
change: [+0.1911% +1.9279% +3.9791%] (p = 0.04 < 0.05)
Change within noise threshold.
Found 11 outliers among 100 measurements (11.00%)
11 (11.00%) high severe
time: [222.39 ns 226.60 ns 231.63 ns]
И время тут измеряется наносекундами все еще.
Это абсурдно долго для просто цикла с парочкой выделений памяти. Поэтому, собственно, пытаемся получить от тебя полный код того как ты меряешь, потому что 90% что у тебя ошибка в подходе к измерению времени, т.е. это все совсем не вопрос оптимизации кода.
UPD: Даже если внутри тестируемой функции взять и выделить вектор из десяти тысяч строк, речь все равно идет и us, т.е. микросекундах:
let vector: Vec<&str> = ["1.0", "2.0", "3.0"].into_iter().cycle().map(|s| *s).take(10_000).collect();