Я искал сквозь Github, когда я нашел генератор пароля ( Omgopass ), что предположительно было намного быстрее, что другие альтернативы. В 600 раз быстрее, чем Пароль-генератор Анкет
Это ориентир Омгопассы:
Увидев это, я вспомнил, что сделал генератор паролей через пару недель назад и не выполнил никаких тестов, поэтому я решил проверить свой подход с помощью этих других библиотек.
К моему удивлению, он сделал довольно хорошо, забил второе место в том же ориентировании, как показано выше. Довольно хорошо, чтобы даже не пытаться.
Конфликт с моим генератором проходов (Passgenny):
Соображения
Этот тест не является отражением качества библиотеки или навыков разработчиков, чтобы действительно была уверена, что нагрузка больше тестов и ориентиров.
Кроме того, функции варьируются от одной библиотеки к другой, одну – читаемые, а одно нет. Некоторые используют крипто для случайных, некоторые нет.
С этим говорим,
Давайте сделаем Passgenny быстрее
Я решил пойти пойти и попробовать оптимизировать его, давайте посмотрим оригинальный код:
class PasswordGenerator {
static upperCaseChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
static lowerCaseChars = 'abcdefghijklmnopqrstuvwxyz'.split('');
static symbolsChars = '<>[]{}=?()&%$#@!¡?¿*_-.:;,'.split('');
static numbersString = '0123456789'.split('');
constructor(options = {}) {
this.options = {
uppercase: true,
lowercase: true,
symbols: false,
numbers: true,
readable: false,
length: 12,
...options,
};
}
updateOptions(newOptions = {}) {
this.options = {
...this.options,
...newOptions,
};
}
random(min = 0, max = 10) {
return Math.floor(
Math.random() * (max - min) + min
);
}
_getCharactersForOptions() {
const combinedCaracters = [];
if (this.options.lowercase)
combinedCaracters.push(...PasswordGenerator.lowerCaseChars);
if (this.options.uppercase)
combinedCaracters.push(...PasswordGenerator.upperCaseChars);
if (this.options.symbols)
combinedCaracters.push(...PasswordGenerator.symbolsChars);
if (this.options.numbers)
combinedCaracters.push(...PasswordGenerator.numbersString);
return combinedCaracters;
}
generate() {
let combinedCaracters = this._getCharactersForOptions();
let password = '';
for (let c = 0; c < this.options.length; c++) {
password += combinedCaracters[this.random(0, combinedCaracters.length)];
}
return password;
}
}
То, что делает этот класс, из набора вариантов, он будет генерировать пароли. Это делает это путем объединения всех разрешенных символов (по параметрам) в один массив, затем мы переиграем длину пароля (определено параметрами) и получите случайную символ из этого массива.
Достаточно простого правильно? Теперь я думаю, что мы могли бы оптимизировать это совсем немного, будем ли мы?
Оптимизация 1.
Хорошо, первое, что я заметил, в _getCharacterForoptions. Я использую массивы, чтобы держать действительные символы. Использование оператора распространения для добавления их в Комбинированные скрепки множество.
Это довольно избыточно, так как мы могли бы использовать строку. И объединение строки намного дешевле, что объединяет массивы.
Давайте посмотрим, что мы могли бы изменить.
Сначала нам нужно изменить, как мы храним персонажей, нам не нужно их разбивать:
class PasswordGenerator {
static upperCaseChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
static lowerCaseChars = 'abcdefghijklmnopqrstuvwxyz';
static symbolsChars = '<>[]{}=?()&%$#@!¡?¿*_-.:;,';
static numbersString = '0123456789';
// ... more code
}
Отлично, теперь давайте изменим _getCharacterForoptions. Метод:
class PasswordGenerator {
_getCharactersForOptions() {
let combinedCaracters = '';
if (this.options.lowercase)
combinedCaracters += PasswordGeneratorFast1.lowerCaseChars;
if (this.options.uppercase)
combinedCaracters += PasswordGeneratorFast1.upperCaseChars;
if (this.options.symbols)
combinedCaracters += PasswordGeneratorFast1.symbolsChars;
if (this.options.numbers)
combinedCaracters += PasswordGeneratorFast1.numbersString;
return combinedCaracters;
}
}
Обратите внимание, как мы теперь возвращаем строку вместо массива.
Давайте посмотрим, как это делает в ориентире
Черт, я не ожидал такого большого изменения, оно почти удвоилось.
Как видите, в этом конкретном случае строки работают намного лучше, чем массивы.
НО ЖДАТЬ
Я думаю, что могу оптимизировать это еще больше, вы могли бы заметить, что результат _getCharacterForoptions всегда будет одинаковым с одинаковыми вариантами. Это означает, что нам не нужно объединять строку на каждом пароле, нам нужно только генерировать их, если параметры меняются.
Мы могли бы приблизиться к этому пару способов, используя мемузаризацию (возможно, лучше), создавая прокси вокруг объекта или простой подход, который я покажу вам дальше.
Оптимизация 2.
То, что я сделаю, сделайте варианты частным и заставить людей изменить варианты, используя UpdateOptions метод. Это позволит мне отметить, если варианты изменились.
Давайте посмотрим полный пример, и я сломаю его потом:
class PasswordGeneratorFast2 {
static upperCaseChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
static lowerCaseChars = 'abcdefghijklmnopqrstuvwxyz';
static symbolsChars = '<>[]{}=?()&%$#@!¡?¿*_-.:;,';
static numbersString = '0123456789';
constructor(options = {}, randomFn) {
this._options = {
uppercase: true,
lowercase: true,
symbols: false,
numbers: true,
readable: false,
length: 12,
...options,
};
this._random = randomFn || mathRandom;
this._combinedCharacters = '';
this._optionsChanged = true;
this._getCharactersForOptions();
}
updateOptions(newOptions = {}) {
this._options = {
...this._options,
...newOptions,
};
this._optionsChanged = true;
}
generate() {
const combinedCaracters = this._getCharactersForOptions();
const length = combinedCaracters.length;
let password = '';
for (let c = 0; c < this._options.length; c++) {
password = password.concat(combinedCaracters[this._random(0, length)]);
}
return password;
}
_getCharactersForOptions() {
// If options have not changed, we can return the previoulsy combined characters
if (!this._optionsChanged) return this._combinedCharacters;
let combinedCaracters = '';
if (this._options.lowercase)
combinedCaracters += PasswordGeneratorFast1.lowerCaseChars;
if (this._options.uppercase)
combinedCaracters += PasswordGeneratorFast1.upperCaseChars;
if (this._options.symbols)
combinedCaracters += PasswordGeneratorFast1.symbolsChars;
if (this._options.numbers)
combinedCaracters += PasswordGeneratorFast1.numbersString;
// Update and mark options as not changed
this._combinedCharacters = combinedCaracters;
this._optionsChanged = false;
return this._combinedCharacters;
}
}
- Мы добавляем
_Optionschangedуказывает, изменились ли варианты с прошлого года_getCharacterForoptionsназывался. - Мы храним последний комбинированный персонаж в
_combiedCharacters - Модифицируем
_getCharacterForoptionsТак что, если варианты не изменились, мы возвращаем последнюю сгенерированную_combiedCharacters - Мы меняем
пароль +=сpassword.concat ()( в моих тестах, он выполнил лучше, что += )
Вот и все, давайте посмотрим, как это сделал:
Впечатляюще, если вы спросите меня, мы сделали Passgenny Более того, что в два раза быстрее, забивая сначала довольно немного полем. Если мы выразим это как Omgovich сделал, Passgenny 2,444 раза быстрее, чем генератор паролей
Что брать от этого?
- Сохранение его просто может приравнивать к исполнению
- Не используйте массивы, если вам не нужно к
- Проверьте, необходимо ли выполнять операции каждый раз
- Если вам нужна производительность, иногда меньшие вещи имеют наибольшую разницу
ПД: Я не эксперт по производительности, поэтому я мог бы упустить какую -то важную вещь, пожалуйста, дайте мне знать, если я что -то пропустил, или я неверно истолковал результаты.
Хорошего дня!
Оригинал: “https://dev.to/nombrekeff/i-created-one-of-the-fastests-node-js-password-generators-without-knowing-16ll”