<commit_before># Як заміряти час

Вимірювання часу виконання алгоритмів для вибору оптимального рішення.

Проблема може мати більше одного рішення і вимірювання часу виконання є інструментом для вибору найкращого рішення.

### Замір часу

Перед початком виконання алгоритму визначіть поточний час за допомогою рутини `_.time.now()` чи альтернативно `Date.now()`.

``` js
var time = _.time.now();
```

Результат збережіть в змінну чи константу. Після виконання алгоритму отримайте різницю часу за допомогою рутини `_.time.spent()`.

```js
_.time.spent( time );
```

Альтернативно можливо повторно викликати `_.time.now()` і знайти різницю між двома замірами, але при цьому доведеться робити трансформування одиниць вимірювання часу вручну.

### Приклад

Для демонстрації техніки заміру часу створіть файл `Performance.js` із таким вмістом.

<details>
<summary><u>Код файла <code>Performance.js</code></u></summary>

``` js
let _ = require( 'wTools' );
let times = 10;
let size = 50000000;
let array = new U8x( size );

var counter = 0;
var time = _.time.now();
for( let i = times ; i > 0; i-- )
var result = forLoop( array, () => counter += 1 );
console.log( `For loop took ${_.time.spent( time )} on Njs ${process.version}` );
console.info( `Output ${counter} to avoid unwanted optimization` );

var counter = 0;
var time = _.time.now();
for( let i = times ; i > 0; i-- )
var result = forEach( array, () => counter += 1 );
console.log( `For each took ${_.time.spent( time )} on Njs ${process.version}` );
console.info( `Output ${counter} to avoid unwanted optimization` );

function forLoop( src, onEach )
{
  for( let k = 0 ; k < src.length ; k++ )
  onEach( src[ k ], k, src );
  return src
}

function forEach( src, onEach )
{
  src.forEach( ( e, k, src ) => onEach( e, k, src ) );
  return src;
}
```

</details>

Код приведений вище має 2-ві реалізації ітерування елементів масиву: `forLoop` та `forEach`.
Тестова програма має складатися з 2-х кейсів ( може бути написана як 1 кейс, потім шляхом зміни розміру сутності та кількості ітерацій - запустити код, щоб сгенерувати результати для другого кейсу ):
- Великий розмір сутності, мала кількість ітерацій ( замір циклічної частини, в основному код, що виконується на сутності );
- Малий розмір сутності, велика кількість ітерацій ( замір нециклічної частини, в основному код, що виконуєтсья за межами сутності );

Розмір сутності та кількість ітерацій мають бути вибрані такі, щоб давати схожий результат, з мінімальної різницею між запусками.

В програмі вимірюється час необхідний для перебору елементів масиву довжиною 50 000 000 елементів, кількість ітерацій - 10. 
Також в програмі вимірюється час необхідний для перебору елементів масиву довжиною 500 елементів, кількість ітерацій - 100000.

Програма виводить змінну `counter` щоб уникнути небажаної оптимізації.

Після завершення тесту, слід проаналізувати час виконання програми та впевнитися, що програма займає стільки ж часу, скільки поєднаний час виконання кожного з тестів.
### Результати замірів

Результати вимірювання слід зібрати в табличку із вказанням інтерпретатора та його версії. Результати можуть суттєво відрізнятися в залежності від інтерпретатора то його версії. Також тест кейс має бути зазначений.

Можливе використання аббревіатур :

- ВСМІ = Велика сутність( length : \<length> ), мала кількість ітерацій ( size : \<size> )
- МСВІ = Мала сутність( length : \<length> ), велика кількість ітерацій ( size : \<size> )

| Interpretor  | forLoop( ВСМІ ) | forEach(ВСМІ) | forLoop( МСВІ ) | forEach(МСВІ) |
| ------------ | --------------- | ------------- | --------------- | ------------- |
| Njs v10.16.0 | 9.940 s         | 17.410 s      | 0.134 s         | 0.728 s       |
| Njs v11.3.0  | 10.182 s        | 17.403 s      | 0.137 s         | 0.758 s       |
| Njs v12.7.0  | 11.533 s        | 21.673 s      | 0.134 s         | 0.801 s       |
| Njs v13.3.0  | 11.448 s        | 21.324 s      | 0.135 s         | 0.811 s       |

Звіт показує, що цикл `for` працює у 2 рази швидше за рутину `forEach`. На основі цього можна рекомендувати використати 1-ий варіант реалізації алгоритму.

### Достовірність та помилки

Правдивість результатів вимірювання гарантувати не просто, проте можливо уникнути деяких часто повторюваних помилок. Результати заміру часу є достовірними якщо за ними можливо робити прогнози по швидкодійності алгоритму.

Для високої достовірності результатів потрібно мінімізувати вплив середовища. Важкі задачі, що виконуються у фоновому режимі на вашій машині, можуть суттєво спотворити результат. Результати не можна порівнювати якщо вони були отримані за різних умов. Якщо змінюється середовище то і результат змінюється. Не можна порівнювати результати отримані на різних машинах. Проте, попри дотримання всіх наведених рекомендацій, все одно залишається вплив випадку і неконтрольовані фактори, тому вимірювання потрібно робити декілька разів і брати середнє або сумарне значення.

Вимірювання не може бути достовірним якщо проміжок часу, який замірюється, всього мілісекунда. Правило пальця: проміжок має бути не менше однієї секунди.

Інша помилка, яку можна допустити при вимірюванні - це включення часу генерації тестових даних до часу виконання алгоритму. Генерація даних займає час. Дані треба підготувати до початку заміру часу і використати, коли вони будуть потрібні.

Оптимізація інтерпретатором ще один підводний камінь. Інтерпретатор може викинути частину коду, якщо помітить, що він не використовується. Тому алгоритм який тестується повинен мати коректні аргументи, а результат виконання має якось використовуватися.

### Підсумок

- Час виконання можливо виміряти за допомогою пари рутин `_.time.now` та `_.time.spent`.<commit_msg>Extend ukr tutorial 3<commit_after># Як заміряти час

Вимірювання часу виконання алгоритмів для вибору оптимального рішення.

Проблема може мати більше одного рішення і вимірювання часу виконання є інструментом для вибору найкращого рішення.

### Замір часу

Перед початком виконання алгоритму визначіть поточний час за допомогою рутини `_.time.now()` чи альтернативно `Date.now()`.

``` js
var time = _.time.now();
```

Результат збережіть в змінну чи константу. Після виконання алгоритму отримайте різницю часу за допомогою рутини `_.time.spent()`.

```js
_.time.spent( time );
```

Альтернативно можливо повторно викликати `_.time.now()` і знайти різницю між двома замірами, але при цьому доведеться робити трансформування одиниць вимірювання часу вручну.

### Приклад

Для демонстрації техніки заміру часу створіть файл `Performance.js` із таким вмістом.

<details>
<summary><u>Код файла <code>Performance.js</code></u></summary>

``` js
let _ = require( 'wTools' );
let times = 10;
let size = 50000000;
let array = new U8x( size );

var counter = 0;
var time = _.time.now();
for( let i = times ; i > 0; i-- )
var result = forLoop( array, () => counter += 1 );
console.log( `For loop took ${_.time.spent( time )} on Njs ${process.version}` );
console.info( `Output ${counter} to avoid unwanted optimization` );

var counter = 0;
var time = _.time.now();
for( let i = times ; i > 0; i-- )
var result = forEach( array, () => counter += 1 );
console.log( `For each took ${_.time.spent( time )} on Njs ${process.version}` );
console.info( `Output ${counter} to avoid unwanted optimization` );

function forLoop( src, onEach )
{
  for( let k = 0 ; k < src.length ; k++ )
  onEach( src[ k ], k, src );
  return src
}

function forEach( src, onEach )
{
  src.forEach( ( e, k, src ) => onEach( e, k, src ) );
  return src;
}
```

</details>

Код приведений вище має 2-ві реалізації ітерування елементів масиву: `forLoop` та `forEach`.
Тестова програма має складатися з 2-х кейсів ( може бути написана як 1 кейс, потім шляхом зміни розміру сутності та кількості ітерацій - запустити код, щоб сгенерувати результати для другого кейсу ):
- Великий розмір сутності, мала кількість ітерацій ( замір циклічної частини, в основному код, що виконується на сутності );
- Малий розмір сутності, велика кількість ітерацій ( замір нециклічної частини, в основному код, що виконуєтсья за межами сутності );

Розмір сутності та кількість ітерацій мають бути вибрані такі, щоб давати схожий результат, з мінімальної різницею між запусками.

В програмі вимірюється час необхідний для перебору елементів масиву довжиною 50 000 000 елементів, кількість ітерацій - 10. 
Також в програмі вимірюється час необхідний для перебору елементів масиву довжиною 500 елементів, кількість ітерацій - 100000.

Програма виводить змінну `counter` щоб уникнути небажаної оптимізації.

Після завершення тесту, слід проаналізувати час виконання програми та впевнитися, що програма займає стільки ж часу, скільки поєднаний час виконання кожного з тестів.
### Результати замірів

Результати вимірювання слід зібрати в табличку із вказанням інтерпретатора та його версії. Результати можуть суттєво відрізнятися в залежності від інтерпретатора то його версії. Також тест кейс має бути зазначений.

Можливе використання аббревіатур :

- BASI = Велика сутність( length : \<length> ), мала кількість ітерацій ( size : \<size> )
- SABI = Мала сутність( length : \<length> ), велика кількість ітерацій ( size : \<size> )

| Interpretor  | forLoop( BASI ) | forEach(BASI) | forLoop( SABI ) | forEach(SABI) |
| ------------ | --------------- | ------------- | --------------- | ------------- |
| Njs v10.16.0 | 9.940 s         | 17.410 s      | 0.134 s         | 0.728 s       |
| Njs v11.3.0  | 10.182 s        | 17.403 s      | 0.137 s         | 0.758 s       |
| Njs v12.7.0  | 11.533 s        | 21.673 s      | 0.134 s         | 0.801 s       |
| Njs v13.3.0  | 11.448 s        | 21.324 s      | 0.135 s         | 0.811 s       |

Звіт показує, що цикл `for` працює у 2 рази швидше за рутину `forEach`. На основі цього можна рекомендувати використати 1-ий варіант реалізації алгоритму.

### Достовірність та помилки

Правдивість результатів вимірювання гарантувати не просто, проте можливо уникнути деяких часто повторюваних помилок. Результати заміру часу є достовірними якщо за ними можливо робити прогнози по швидкодійності алгоритму.

Для високої достовірності результатів потрібно мінімізувати вплив середовища. Важкі задачі, що виконуються у фоновому режимі на вашій машині, можуть суттєво спотворити результат. Результати не можна порівнювати якщо вони були отримані за різних умов. Якщо змінюється середовище то і результат змінюється. Не можна порівнювати результати отримані на різних машинах. Проте, попри дотримання всіх наведених рекомендацій, все одно залишається вплив випадку і неконтрольовані фактори, тому вимірювання потрібно робити декілька разів і брати середнє або сумарне значення.

Вимірювання не може бути достовірним якщо проміжок часу, який замірюється, всього мілісекунда. Правило пальця: проміжок має бути не менше однієї секунди.

Інша помилка, яку можна допустити при вимірюванні - це включення часу генерації тестових даних до часу виконання алгоритму. Генерація даних займає час. Дані треба підготувати до початку заміру часу і використати, коли вони будуть потрібні.

Оптимізація інтерпретатором ще один підводний камінь. Інтерпретатор може викинути частину коду, якщо помітить, що він не використовується. Тому алгоритм який тестується повинен мати коректні аргументи, а результат виконання має якось використовуватися.

### Підсумок

- Час виконання можливо виміряти за допомогою пари рутин `_.time.now` та `_.time.spent`.
{'fraction_non_alphanumeric': 0.09234273698966182, 'fraction_numerical': 0.02286665498510601, 'mean_word_length': 3.836864406779661, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3292911', 'n_tokens_mistral': 4767, 'n_tokens_neox': 5922, 'n_words': 1410}