TGViewer
codaza codaza @codaza · 4.59K subscribers
Post #51 4.56K
#codaza_отвечает

Что же... и в этот раз, коллективными усилиями, предложенный фрагмент кода, без преувеличения, был разобран по косточкам. В нашем сообществе не мало действительно профессиональных разработчиков. Если вы не успели пробежаться по комментариям в предыдущем посте, то прошу вас не полениться и сделать это, так как вы увидите развитие мысли и ссылки на дополнительную информацию от участников группы.

Где же был зарыт "главный слон" 🐘? Проблема, с которой данный код никак нельзя было допускать в production-среду. Проблема была на строках 12-13:

int keyWordIndex = dataSlice.Value.TextFraction.Substring(actualPosition).IndexOf(keyWord);

Вызов метода Substring() - вот основная проблема. Дело в том, что при каждом вызове этого метода, в памяти будет создаваться полная копия строки, содержащаяся в свойстве TextFraction. Это не проблема при нечастных вызовах на небольших строках и без использования циклов. Для нашей ситуации это совершенно не годится, так как методу предстоит работать с большим массивом данных. Только представьте что может произойти с оперативной памятью при частых вызовах метода ExtractStochasticDataAsync(). И первым проблему заметил Александр Максименко, указав на то что вместо вызова Substring() следует делать вызов метода расширения для класса String - AsSpan(). Такой подход позволит не создавать копию строки TextFraction в оперативной памяти, что существенно снизит нагрузку на неё.

Далее, Александр увидел возможность для параллельной обработки данных в словаре dataSlices. При параллельной обработке нужно учитывать проблемы совместного доступа, поэтому, для словаря indexes, вместо Dictionary<int, int>, Александр взял потокобезопасную коллекцию ConcurrentDictionary<int, int>.

Метод ExtractStochasticDataAsync() является асинхронным и хорошей практикой является предоставления возможности для остановки метода (тем более метод работает в цикле с большим объемом данных). На эту проблему обратил внимание Aleksandr Radchenko, предложив использование CancellationToken.

Итоговый вариант после рефакторинга стал выглядеть так:

public async Task<IDictionary<int, int>> ExtractStochasticDataAsync(string keyWord, CancellationToken ct = default)
{
IDictionary<int, AnalyticalDataSlice> dataSlices = await LoadAnalyticalDataSlicesAsync(ct);

ConcurrentDictionary<int, int> indexes = new();

var tasks = dataSlices.Select(async ds =>
{
ct.ThrowIfCancellationRequested();

int actualPosition = await LoadActualPositionAsync(ct);

int keyWordIndex = ds.Value.TextFraction
.AsSpan(actualPosition).IndexOf(keyWord);

indexes.TryAdd(ds.Key, keyWordIndex + actualPosition);
});

await Task.WhenAll(tasks);

return indexes;
}

Кроме того, Aleksandr Radchenko предложил еще ряд важных улучшений с примерами листингов (за что ему отдельное спасибо). Например, использовать в качестве возвращаемого значения метода ExtractStochasticDataAsync() асинхронную реализацию интерфейса IAsyncEnumerable и оператор yield, что позволит организовать асинхронную обработку данных из вызываемого метода. Это потребует изменение выходного API у метода ExtractStochasticDataAsync(), но если это возможно по имеющимся бизнес-требованиям, сделать это точно стоит. Этот вариант будет выглядеть так:

public async IAsyncEnumerable<(int Key, int Position)> ExtractStochasticDataAsync01(string keyWord, [EnumeratorCancellation] CancellationToken ct = default)
{
IDictionary<int, AnaliticalDataSlice> dataSlices = await LoadAnalyticalDataSlicesAsync(ct);

foreach(var dataSlice in dataSlices)
{
int actualPosition = await LoadActualPositionAsync(ct);

int keyWordIndex = dataSlice.Value.TextFraction
.AsSpan(actualPosition).IndexOf(keyWord);

ct.ThrowIfCancellationRequested();

yield return (dataSlice.Key, keyWordIndex + actualPosition);
};
}

Из таких code review можно получить гораздо больше ценных и практических знаний нежели из очередной статьи в интернете.

Всем активным участникам огромное спасибо за вклад! 💙
  • 👍 67
More from @codaza
  1. Oct 28, 2022#капитану_на_заметку Всем привет! 👻 Понятие "качественный код" очень многогранно. Одна из…
  2. Oct 5, 2022#капитану_на_заметку Всем привет! Рефакторить код и визуально уменьшать синтаксические кон…
  3. Sep 14, 2022#капитану_на_заметку Всем привет! Мне нравится использовать подход Code First. Особенно уд…
  4. Sep 7, 2022#капитану_на_заметку Всем привет! Нельзя сказать, что генерация исключений в C# выглядит и…
  5. Aug 31, 2022#капитану_на_заметку Всем привет! Комментирование исходного кода — это здорово! Открыл исх…
  6. Aug 24, 2022#капитану_на_заметку Всем привет! Большинство web-сервисов предоставляют возможности для и…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →