Зачастую в программисткой практике необходимо нагенерировать множество случайных строк. Либо для тестового примера, либо как источник обезличивания, либо просто, чтобы наполнить разработческую БД. Задача, в принципе, понятная и легкая для любого уровня программиста. Но если это нужно сделать быстро, например, если набор случайных строк нужен здесь и сейчас, то можно использовать предлагаемое решение. Строки получаются разной длины, со 100%-ной хаотичностью (полностью несортированные). Выглядят эти строки вот так (спойлер):
Скрытый текст
92fmUw0+0eLE71rw/v3oRrQhMC13Wn13xwI3irx8g/br4sa2viHHkgfN0CauOTILV
DsqCACqg3Ovgdm7aPhkeEzR7pDAb1iOZ+H1YRWFKMOwP+05P5
ivzUR4Xr8bf4dsZJ1eKegv66U/ZmX6AtoU7clmxtMdsYLGFz/OAnHspvPDIFKk+gLLO6g8I+l50
Y9/IfrOrJXVqM7qCrpDUNYos5s60Ytg1smoms
8fXbwDJBacppg/nL2nXMm30dSkve
iadlICPj9XO7fVP3Pgz07oF8jTeIdkxdYzOLTP/wvBDRPbf+nk4PXIeAwkO
/CO/Nl/TW/4qjiq
udaFbaEf/1WaJv1tVKhJ29qZezZzw9pal3O
4cL+Fwp5usrOtQbzrV2YdyItGSu9sCHI373Gn2Dwa4nCyHiWGhZW6aN0MAjqz25NBPI6IG1B8Uv
XqUlJRdUzA47+FrOrgOYkbgZ4e06
QmTrgyhW9Z3acH8HiDpik2gW1jq7
7IGB1u2e
wK
qBKf8s
ZOTSvR7dTkar0aPkGdysGWO5cwnSsLe487DR1FGFDofyXFr8zGsd6m60NBA3khHu1
ZyTi7TujA
WZnugfmJOu0OgiSO2q29SdJlrolYV/g
i+c6P2107ISNXngrL
jOusrKAjxyLLu07vY931
RE4jWmwl1yqyHe16UZlA3S72SRSSz2IZKCes9DWw
H3Gd7iz+zu6pPzzKEbAt5vC9
nn
RFbx38
EmfkliXQJD1laIZECkR3dTNU4E5DgAJq6IFc2LI6pLbQR2uk
v3z9n+8p9CqCElMKJimy8VtYuy/GQnI6ugv0vCM1eQvL0G1GJu
elL9gWFkeOEJJdN95ck965OgWWylejrn/YPB
Ytxj7pK2KnmE5SCy7qTECNQTZOBP2YrM04U4vlrbDL3IwULJgT2
AEUZPfa9T8QFHj60HptfQt/i0QaK
7rb9uJUBYayKZTimZiluyskatbE1+7
crS/+bxR3/OcwQDBAOkDqkvzzIPrdO0MgkkKjGBVRntAi5vHgxNNx+7jA45TbXVuqUQuyt+xah
J1N5jm0mS
dV5Nzne34xFzuC+BELGSKMJh7rtPdc9a+iQKNEiKin
b+y+hdF9qVvD9Kdu/ca9JKeSMK/11Hxm6sFxIk1EKVP5MTK/xSkzi0Xq8BcQSdAQFD5PxFg7TeqZgXhwG
nZFuYMgv7MPtzKTstgO4Vt2rs6fpjI52Co1rXExO1WK
5s6OYiOKm7JXAELOTchbpq
l6OI19uncCf4szXtz
/BPvAM7xSihcYOhpkwHu6YTYjD7LbDbqFjKdw0b6yfPr5wMxsm5u7P4KcL5js8V7VMEUjLwJpANke+PMrn
A9
4YNR7iD7ocFYrxXqnjR0HtYL0AA8lXOothgWI
pqfGlf0tHv+hlCSHGzwJ6QRFZStSh+2I
7ZawvtS4kWh51qlC7Tsgsk1fmPVFP7nTa8PfeKNi/sE8lygnZNjeXEwmiboS0T2dj5RmaluW4+
WqutDV
fdbS6gnspyrP1lijyX/GUKy+HHmtdJjw8Is3OpjDM2zrXvoHJOZGt6zHdG2gLrMbX
qqT4WfALNnDW80p8
7PDy0hmEUtSjHtbMvZu8JgPxKmCRuW//qBIKI+b+ftAnXzAW1QHHzmVZKXK3qXC8Aeh/R5MyJCBUzi5
kHHv70Z4pHdjH9CptqJRRBQWTt
CoL1LrZDFt+y/2EczS69vlh2UxmYg+PyzeIOZlOSAkgNCiiAT5z2z+oQV+3xqw8t94vA5hUkq
tGiooR7ka6zGuGOnC8P9
AEyMpnk5BB
g4h4/gu6WcJBdHhr7orPJjWiY4mFHkkART4H7
WsBwQFEGTH71bKepdaQiNsUO/mW1spSVDfVL2ItxL+wWkJh42zc2t
Ca8lqLTs6raRw8M1DacjdPH21pB2fq9i4XynS
RVdt2N7DSI+
I9WfBQTig0V8t0nMT1VzMKngJu52vdszOV1WSRV
irMsl
+9zcwBO6aU9cMZ2xERMWAqhP8jQbGT
C3b1mEoBIQfSO/zi7BmRh5Yd7HrriJ
Ce9gj8ZDpjz85y8+jXR1flDxSfSAk2hXW4nM07DrHBfGSnbbYzrpTL1JxFMyu6rqWy7/jNaToJFLZI69JrWl
K0jTGQQGlCG3kwkPBCD9/sFofmEHzZ6OhuqgtjWQQibLLI/ow9Gc0WrMr1
6FEXYshDg/CtryRwiqA3Xm6zTR
6OZRpE
l2cHZNvDTuHKq3CHgTAUxxAv/8vLkvCaCNBSOwYMx1Zvt18Qjf41scRSgu1IsOl6vljtk686I2zO9P
3hD3WEGThQdVzHIK4/e4aY3uFAJCqX9jhbe/EBr8tvaqZRDn/408bOtntUD8hGS8mR4RuPvHlHaUXZ0
xiSvhHya/YcAMc3u0JSrTaCS+zou5H/J1UgIvAHnttL8CxSR+EoKt0su3WYjx+5MtrnDyLAtMt7jisqHV5Rb7g
hKgtfxuFmA9rSZFZ4yI/dJPHZRpeCIslgSlaGVPQ6/LRo/RLnkLQaH3Qsds
SPsX12TE4vVEWb1PTzuzZ+JMVoS9wB+P+lbBS80ptpYPgg4v00LBBN8yOfNF8eKBACfjROJiUJ4AzI3UGfFeI
iLBMv85FMTMQcvxnNcj7f/JN0HUZ6DuAVrhrVoow
KwhvfvqRSRsWHq2A8z/59xAJcPzzA+VzOnuxUUD/1lHJk2d
jZKMaifMc3MacU9jqR+PYs0n07d6
TQIEkq33MI
UyvkSM0LiOcVDFUSMgfrJBEvrH6
atTMiuWdNXMG3MOCEqCfeoe+c8/VGMUJN6O3+TzXzVnOH/cRV9O/5zwhaj7OFGHpm8thO34I2UHABss3OO
LPtWTmAGT
IW9elzSUoN+pQkjM/W6UTBgWdf8sTxACMBzQQSFrIMwS
e6LNWdEqVk6TlgDifSxBavXAUqUTWBq7P8jYWweXx1DfUGy
pFqXPIKukbtti0BpKxj9zS9Nx7llcy5wENTNUJnnp6D3zJfq582
096diruMhFtAkjd
3/cARuT5Air++ijS3a35Tp1Cvp4qK3J5Z75uV7IMkuH6lGpVm/+DMaa2V7W9nuMsE5VuA7y
FflWDbDlcD/bBTofskxsUfPujCHv6gCfINzMBSzSvq4uYqUvBURV7pXdreVis8OQvXpPVWzWrjTO66mDPnO
ZhnhPpC5qIK3nNvGEWLf0X0LuNKlVr1QEW5/uhOWWQSKZiLT6EqsYqSqXGGdZ+8tlqoF
8GIV24vGJ77Jglj+q8iF5R05mjC1huojq+U3WSAcEw8zvs0sEYK40HNEr4cG4CEMVWYJ12v
FhhnzeRQa7S9vkwmQkG1REAtTyg2TFkkzpbIPj8m2m0oufB8KA6bnBtnWTx
Up82M1q0Su57vZglvqKhe0ZdNlLRcAprrJrWd3SVVb8xsPhd1nS4ADSKykjWtnrQesOAsMEGB5GFkh56C
TEBUaQK063VLPgnh/Lh
THVxwaQ/L4NAL3Lu/0mpf4fhuvdyeP8yX4C4ZzOtUwqhlB3l7lWMKhsIKHu5QiYKlI5Oc09n
lorcigrhM6ImmGzSZBeVP2+9L6nrdvATq30kF6CcZGM4QTsY
8qbESKNeNVuluv0w9lwGxhA3bKHMOFiqjhYRH4KI3ZbooCD
tISGqpGEJgB3909Z+0hHYsUSfWjbPgDEDF6fIANp5TKZGPscJ
1JTlgcnPly8UYZ+ugZmqqwVAzk4+/Lch4MRsvqVjafBogitzj6vEXP+gfHZivpc4LK
jg2DNCfPfFL1Y7qbm34Fz0y
OH68CvNt3rw3U7niP+smj2goEXGIO631cdLO8TnCqt2nkqnQdqjrKcQzW0cwmbmvNAxpb2mNhG
8GKdicvFizZ9AfdKYlttzxft884d9nizIi6nj/SW5nM2s5ql/MtheICFY1HwUjBfw1IiQLwtVR/E8FbE8J2ebw=
............................................................
Вот код на C#, проверялся для .NET8 на Windows 10, компилировался на Visual Studio 2022. Как уже упоминалось, для генерации 100 млн. строк уходит менее минуты на обычном домашнем PC, а точнее 41 секунда:
private static string[] RandomStrings(int count)
{
Random rand = Random.Shared;
ConcurrentBag<string> bag = [];
Parallel.ForEach(Enumerable.Range(1, count), num =>
{
string str = Convert.ToBase64String(SHA512.HashData(Guid.NewGuid().ToByteArray()));
bag.Add(str[..(1 + rand.Next(str.Length - 1))]);
});
return [.. bag];
}
результат:

полученный массив можно дальше использовать по назначению, а если сохранить в файл, то он будет весить в районе 4Гб.
Объяснение алгоритма
Скорость алгоритма объясняется генерацией не символов, а сразу набором байт, и, разумеется, в распараллеливании.
В параллелях генерируются GUIDы, что дает уникальность. Далее этот массив байт подвергается криптохэшированию, что дает полную непохожесть наборов байт (а исходные GUIDы похожи друг на друга, хоть и отличаются). Полученный набор байт переводится в строки, используя base64-кодирование. (Кодирование base64 использовалось только потому, что в моей задаче необходим был такой набор символов. Но ничто не мешает переводить байты в строки любым удобным способом). Затем строки по псевдослучайному числу обрезаются (Random только для этого используется), чтобы строки были разной длины. До обрезания строки абсолютно уникальные. Но после обрезания некоторые короткие строки становятся одинаковыми. Это нужно иметь ввиду, если уникальность важна. Сгенерированные строки сохраняются в потокобезопасную коллекцию, а по завершении генерации копируются в результирующий массив.
Необходимые пространства имен для работы алгоритма (спойлер):
Скрытый текст
using System.Security.Cryptography;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
Рад буду узнать, что это кому-нибудь пригодилось. Если нужно изменить код под ранние версии C#, намекните в комментарии.
Комментарии (43)
fedorro
17.02.2025 11:59Не понятно зачем столько случайных строк, и быстро. Ну и код далеко не самый быстрый, зачем, например, Гуид генерить, в байты его переводить. Можно одно рандомное число взять, к нему неиспользуемый num прибавлять, и от этого хэш считать. Ну или один раз гуид сгенерить и в нем от счетчика байты менять. Да и SHA512 - довольно тормозной хэш, есть и побыстрее, даже в стандартной библиотеке, в описанных юзкейсах настолько криптостойкий хэш и не нужен. Вместо (str.Length - 1) можно взять минимальную возможную длинну - она не сильно то и гуляет.
Вместо ConcurrentBag<string> bag = [];
можно сразу массив строк нужной длинны аллоцировать
и выполнять присвоение
result[num] = str ;
Ну и прочий синтаксический сахар можно оптимизировать.
dnkv Автор
17.02.2025 11:59SHA512 дает нужную максимальную длину итоговых строк.
fedorro
17.02.2025 11:59Можно просто было рандомом 64 байта взять, по желанию крипторандомом System.Security.Cryptography.RandomNumberGenerator.GetBytes(Int32)
по сути тоже самое что генерация гуида и хэша от него, по скорости - быстрее.
AndreyDmitriev
17.02.2025 11:59Вообще говоря это далеко не самый быстрый способ, есть и другие подходы, без хэширования. Мне как-то давно потребовалось генерить видеопоток, заполненный случайным шумом, но так чтобы укладываться в тайминг фреймов (160 МБ в секунду, кажется или около того), я в интернетах быстро нашёл подходящий несложный алгоритм. В данном случае я б Вихрь Мерсенна попробовал прирастить, там надо посмотреть какой период повторения псевдослучайной последовательности, но должно быть ещё быстрее чем данный метод, там просто банально меньше мат операций нужно для получения каждого следующего символа. Просто идея.
dnkv Автор
17.02.2025 11:59Вопрос применимости. Представьте себе ситуацию, когда вы не можете в решении использовать STL, например, собираете CLR-хранимку для MSSQL. А здесь вот - готовый код, только заворачивай в хранимку, и MSSQL получает генератор мусора в 4Гб за пару десятков секунд.
Согласитесь - код простой, понятный и короткий, свою задачу решает. Вот и славно :-)
AgentFire
17.02.2025 11:59Генерация подобного мусора не выглядит, будто бы оно вообще чего-то решает. Больше похоже на XY проблему.
Cheater
17.02.2025 11:59Имхо параллелизм в данном коде на C# не поможет, тк пул задач здесь состоит из 100 млн мелких задач на несколько тактов CPU, программа больше потратит на синхронизацию тредов (при том что непохоже чтобы этот ConcurrentBag был lock free, не знаю как это вообще сделать для массива динамических строк неизвестной длины и непредсказуемой позиции в памяти, думаю он просто потокобезопасный и параллельной записи не происходит). Попробуйте выполнить свой код в 1 поток, очень подозреваю что быстродействие не изменится. Если уж сильно захотеть копать в сторону параллелизации, то имхо в первом приближении сработает грубый способ - написать код, который параллельно генерирует 8 одинаковых массивов размера 100000000/8 элементов (кусков результирующего массива).
На C# не умею писать, вот решение в лоб на C++ без потоков с вихрем Мерсенна в качестве ГПСЧ, выполняется 12 сек на моей машине:
https://godbolt.org/z/bWEodGacvVoodooCat
17.02.2025 11:59Параллелизм в данном коде абсолютно точно помогает, но для того что бы вообще можно было сожрать полностью процессор, - ему нужен серверный GC. С ним на моей машине 15 секунд. Без него - лишь ~40% загрузки CPU и печальное исполнение ~30 секунд.
А вот тупое переписывание в более современном виде просто избегая ненужных аллокаций (ниже приведу код) - даёт уже 14 секунд и 10 секунд с серверным GC, однако при лимите параллелизации в 6 потоков: и почему именно 6? Экспериментально подобрал. Если не ограничивать, то мой 16-ядерник полностью выжрается, однако быстрее результат никак от этого не становится - а это говорит нам о том, что за бездумное использование Parallel.ForEach надо бить ссанными тапками по рукам, и почти всегда это так. А, и да - однопочная версия у меня исполняется около ~40 секунд.
Да, ещё в C# - строки UTF16, в вашем примере это не так. Но вообще сранивать тут бесполезно: если хочется прямо 100 миллионов строк массово - то упираемся в аллокатор, но зачем они нужны массивом вот так сразу - большой вопрос, тоже самое можно генерировать по мере необходимости.
Улучшения в лоб:
private static string[] RandomStrings2(int count) { var result = new string[count]; Parallel.ForEach(Enumerable.Range(0, count), new ParallelOptions { MaxDegreeOfParallelism = 6 }, num => { Span<byte> guid = stackalloc byte[16]; Span<byte> hash = stackalloc byte[512 / 8]; Span<char> chars = stackalloc char[512 / 8 * 4]; if (!Guid.NewGuid().TryWriteBytes(guid)) throw new InvalidOperationException(); if (SHA512.HashData(guid, hash) != hash.Length) throw new InvalidOperationException(); if (!Convert.TryToBase64Chars(hash, chars, out var charsWritten)) throw new InvalidOperationException(); result[num] = chars[..(1 + Random.Shared.Next(charsWritten - 1))].ToString(); }); return result; }
dnkv Автор
17.02.2025 11:59Любопытно. Проверю на своем слабом PC, насколько улучшится время
VoodooCat
17.02.2025 11:59Вы проверьте и нам доложите. :)
Но вообще мой комментарий был адресован @Cheater - человек не разбирается в C#, и мне хотелось ему показать, что современный C#+его stdlib (BCL) может не полагаться на выделение памяти на каждый чих, оставаться достаточно читаемым - и при желании можно добиться очень хороших результатов - более того - это не сложно, нужно только захотеть попытаться это сделать, что я и сделал. Это была моя основная цель. Я просто не донёс её в комментарии правильно/ясно.
Вас же, как автора статьи - ничем не хотел обидеть и про ссанные тряпки - это просто наблюдения личной практики - но она может различаться с другой практикой.
Так же хотел про lock-free повещать... но боюсь я останусь не понят. Если кратко, то большинство lock-free коллекций - просто имеют спин-лок на основе CAS, и этот спин естественно не умеет апгрейдится в нормальное событие от ОС. Так что применение "обычного" lock + партиционирование - мне лично видится куда более лучши вариантом чем бесконечные спины которые масштабируются ОЧЕНЬ ПЛОХО -> потому что в C# обычный лок это и есть спин + апгрейд до события. Вот. Ну, я не хотел говорить, и поэтому про это и не говорю.
PS: В задаче анонимизации допустим БД - предположу, что возможно нужны где-то несколько строк разом, но что бы разом нужны были миллионы - да нет. Непонятно зачем генерировать это массово, что б потом поменять на хэши. Ну да ладно, может и есть какое применение, но встаёт вопрос - так ли важна материализация, или может вам сразу выделить 1GB (с огромным запасом) случайных символов и из них натыкать строчек случайных и то слайсами и то по необходимости, или отложить материализацию. В общем, не имею ничего против предложенного, но опять же лично у меня ~5 секунд тратится только лишь на выделение памяти, если без хэшей и прочей ебалды. Люди уже предлагали тут варианты которые позволят приблизиться к этому. А если ещё убрать выделение то формально время сократиться к 0. :) Ну, как-то так.
dnkv Автор
17.02.2025 11:59В следующей моей статье (пишется) будет понятно зачем понадобилось нагенерировать много строк на ходу :-) Выдернуто как отдельный интересный момент, как мне показалось.
dnkv Автор
17.02.2025 11:59А вот вы в стеке аллокируете память. Это понятно, что быстрее, чем в куче. Однако, риски нехватки памяти (в данном случае переполнение стека) многократно возрастают. Может даже и не хватить для генерации 100 млн строк на маломощном PC. Когда вы так делаете - нет опасения развития таких неблагоприятных событий?
viruseg
17.02.2025 11:59Попробуйте добавить атрибут [SkipLocalsInit], чтобы избежать постоянной инициализации нулями span'ов. Должно стать быстрее.
SLenik
17.02.2025 11:59Спасибо за статью!
Кладу код в копилочку примеров на собеседования, на котором после показа кода первый вопрос к кандидату будет: что бы кандидат предложил переписать для уменьшения времени работы кода.
PS. А вообще выглядит как план: сначала выложить код в виде статьи на хабр, а далее собрать из комментариев идеи по его улучшению и реализовать самые лучшие.
dnkv Автор
17.02.2025 11:59Идея для собеседования:
есть последовательность цифр с пропусками. Нужно наиболее эффективным способом выбрать все пропуски в виде диапазонов:
5-9
17-23
33
45-46Годится для любого языка. А на диалектах SQL - так вообще огромный простор для творчества.
NikkiG
17.02.2025 11:59А в чем сложность? Вроде бы короче чем за один обход не получится, а за один решается в лоб
dnkv Автор
17.02.2025 11:59Так на собеседовании и не должно быть ничего сложного.
А так:
1. Пусть кандидат попробует обойтись без явных циклов, оперируя множествами.
2. Пусть кандидат попробует обойтись без накопительного по одному диапазону списка диапазона, а получить сразу список, оперируя множествами.
В основном, конечно, такие задачки на собеседованиях по SQL. Но на ЯП тоже годится.
SLenik
17.02.2025 11:59И пара моментов по тексту статьи:
В параллелях генерируются GUIDы, что дает уникальность.
Вообще говоря, реализация метода Guid.NewGuid() зависит от операционной системы:
в Windows Guid создаётся силами native-библиотеки ole32.dll (метод CoCreateGuid) (исходный код .NET 9). На rsdn есть древняя статья, в которой препарировали реализацию CoCreateGuid того времени. И обнаружили там достаточно предсказуемые данные.
в Linux же Guid может создаться как через вызов криптографического генератора случайных чисел, так и обычного (снова исходный код .NET 9).
Вывод 1: считать сгенерированные данным кодом Guid-ы случайными можно только в определённом окружении.
Далее этот массив байт подвергается криптохэшированию, что дает полную непохожесть наборов байт (а исходные GUIDы похожи друг на друга, хоть и отличаются).
Любая функция хэширования обладает свойством детерминированности. То есть при передаче ей одного и того же значения она будет возвращать один и тот же результат. Посчитайте хоть один раз, хоть миллион любую функцию хэша от Guid.Parse("be6296a3-fc24-4f58-a450-a874a333f8cd") - результат всегда будет одним и тем же.
То есть если взять периодическую последовательность и прохэшировать её блоками фиксированного размера (в вашем случае по 12 байт), в результате вы также получите периодическую последовательность.
Вывод 2: реализованное хэширование не увеличит "случайность" в ваших исходных данных - поэтому его можно вообще удалить из кода.
UPD: увидел комментарий
SHA512 дает нужную максимальную длину итоговых строк.
Для выравнивания длины строки использование хэша идея не самая хорошая, уж проще случайными байтами дозаполнять.
Или сразу генерировать нужное количество байт через упомянутый выше System.Security.Cryptography.RandomNumberGenerator
dnkv Автор
17.02.2025 11:59Добиться какой-то непредсказуемости, криптостойкойсти задачи не было. Вполне достаточно было уникальности и непохожести. Поэтому guid и крипттохеш. Нужен был просто генератор мусора, выполняемый относительно быстро.
А так-то - сделал бы сам генераторы гаммы по ARC4 в потоках и нарезал бы на строки. Тем более, что делал уже лет 10 назад :-)
Прелесть примера - в его тупой простоте и краткости. В этом вижу некую элегантность.
sdramare
17.02.2025 11:59Зачем вам нужны гуиды, хэши и кодирование в base64, если вы можете просто определить алфавит и через случайную выборку символа и shuffle создавать строчки нужной длины?
dnkv Автор
17.02.2025 11:59Просто посчитал, что генерировать по-символьно - не элегантно.
sdramare
17.02.2025 11:59Ну я вот проверил вариант без всего этого - он работает в буквально смысле в 10 раз быстрее. Если скорость работы не стояла целью, то зачем было параллелить?
private static string[] RandomStrings(int count) { const int maxLen = 88; // Convert.ToBase64String(SHA512.HashData(Guid.NewGuid().ToByteArray())).Length; const string base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var result = new string[count]; Parallel.For(0, count, i => { var stringLength = 1 + Random.Shared.Next(maxLen); result[i] = string.Create(stringLength, base64Chars, static (span, state) => { for (int j = 0; j < span.Length; j++) { span[j] = state[Random.Shared.Next(state.Length)]; } }); }); return result; }
dnkv Автор
17.02.2025 11:59Мне хотелось избежать посимвольной генерации. Посчитал не элегантным.
dukei
17.02.2025 11:59Так надо было статью тогда называть "Медленный, но элегантный способ сгенерировать 100 млн строк".
А вообще, мне кажется, элегантность состоит в решении задачи минимальным количеством действий и инструментов, а не привлечением громоздких и ненужных.
dnkv Автор
17.02.2025 11:59Насчет "медленный", пожалуй, не соглашусь. Это все-таки субъективная оценка. Мне производительность понравилась, вот и решил поделиться.
Нисколько не сомневаюсь, что можно придумать и более быстрые алгоритмы (как и гораздо более медленные :-) ), дающие тот же результат.sdramare
17.02.2025 11:59Тут правильней слово "неэффективный способ". Плюс еще и читается(поддерживается) такой код хуже - алгоритм с выборкой случайного символа из массива поймет даже школьник, который вчера начал изучать программирование. А алгоритм, который считает base64 от хэша sha512 байтового представления Guid требует кучу специфичный знаний от криптографии до особеннностей имплементации UUID в .net на различных платформах, о чем говорил в других комментариях.
dnkv Автор
17.02.2025 11:59Что-то даже не подумал, что этот код может показаться кому-то сложным. Учту. Спасибо.
LaRN
17.02.2025 11:59А если взять томик Война и Мир и зашифровать его любым шифрование. Можно получить несколько мегабайт текста, которые можно нарезать на строки нужного размера.
dnkv Автор
17.02.2025 11:59Как уже упоминал, бывают вопросы применимости. Вот для генерации значительного колва мусора в MSSQL - это готовый пример, остается только завернуть в CLR-хранимку. А вот взять томик Войны и Мир внутри СУБД не всегда возможно :-)
LaRN
17.02.2025 11:59Clr это не очень хороший вариант. Это нужно доступ к серверу иметь. У нас например админы никого туда не пускают и clr сбору поставить - это 7 кругов ада из согласований пройти. Даже на тестовую среду.
dnkv Автор
17.02.2025 11:59CLR - в некоторых случаях единственно возможный вариант. Например, если вам нужно в БД реализации Regex. Или что-то агрегировать, что MSSQL сам не умеет. Например, попробуйте сделать AVG по датам или временам. Не получится :-) Почему-то разработчики MSSQL посчитали, что "средняя дата" или "среднее время" - это что-то невероятное. Редкое - да, но не невероятное. Вот тут только CLR и выручит.
Строго говоря, с датой и временем - не совсем так. Можно откастировать в float, получить AVG, а потом откастировать обратно. Некрасиво, но работает.
sdramare
17.02.2025 11:59вам нужно в БД реализации Regex
Значит у вас проблема систем-дизайна, которую вы решаете костылями на CLR в базе.
Или что-то агрегировать, что MSSQL сам не умеет. Например, попробуйте сделать AVG по датам или временам. Почему-то разработчики MSSQL посчитали, что "средняя дата" или "среднее время" - это что-то невероятное
Потому что OLTP не требует таких агрегаций. А OLAP можно сделать и через касты, как вы правильно указали, но опять же, использование MSSQL для OLAP выглядит как ошибка дизайна, которую опять же пытаются заткнуть костылями типа CLR-хранимок.
dnkv Автор
17.02.2025 11:59Почему "костыль"? Прямо очень категорично. CLR - вполне себе штатные средства разработки в MSSQL, как и Java в Оракле.
sdramare
17.02.2025 11:59Костыль потому что при правильно построенном ETL и DW самой необходимости в SP для OLAP не возникнет. И опять же, зачем MSSQL, когда для этих целей существует кликхайс, апач дорис, трино и т.д. Возможно у вас на это есть весомые причины, но в моем опыте, как правило, такое происходило когда люди делали репорты из того что было и как умели, а потом мужественно решали возникающие проблемы.
dnkv Автор
17.02.2025 11:59Я понял, вы из мира bigdata, поэтому полностью отрицаете серверную логику в СУБД. Однако, существуют другие миры, где серверная логика в СУБД очень развита. Например, многие банковские системы (АБС). Вы же не думаете, что когда в мобильном банке делаете платеж, а остатки у вас тут же обновляются, то эти остатки приходят из DWH ? Нет, это все крутится на основной БД АБС, которая в основном работает как OLTP, но на ней работает много серверной логики, и частично как OLAP для получения оперативной отчетности. Выписки по счету, например, - это она. А уже сбоку - ETL и DWH для отчетности аналитической, финансовой, регуляторной, которая вполне может подождать даже один день.
kekoz
BASE64 — это строки из довольно ограниченного подмножества ASCII, для кого-то это может оказаться весьма критичным.