Я провожу тест о структурах ref и text-ValueType
и сделайте шаг в это сомнение, работая в конструкторах структур.
По-видимому, оператор switch, управляющий переменной ref с помощью цикла в стиле i, может быть
быстрее, чем ручная установка позиций массива в поле структуры.
Я не уверен в достоверности такого теста в методе оператора верхнего уровня, но
вот код
using System.Diagnostics;
const string testText = $"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Quisque orci purus, vulputate vulputate
""";
const int load_Steps = 250;
CharblockExample bySetBlock ;
CharblockExample bySwitchBlock;
var watcher = Stopwatch.StartNew();
var results = new List<(TimeSpan tic1, TimeSpan tic2)>();
try
{
for (int i = 0; i < load_Steps; i++)
{
var start_tics = watcher.Elapsed;
bySwitchBlock = new(testText.AsSpan(), true);
var ticBlock2 = watcher.Elapsed - start_tics;
start_tics = watcher.Elapsed;
bySetBlock = new(testText.AsSpan(), false);
var ticBlock1 = watcher.Elapsed - start_tics;
Console.WriteLine($"{nameof(bySetBlock)} and {nameof(bySwitchBlock)}: {ticBlock1} vs {ticBlock2}");
results.Add((ticBlock1, ticBlock2));
}
}
finally
{
watcher.Stop();
Console.WriteLine();
var overall_1 = results.Sum(element => element.tic1.Microseconds);
var overall_2 = results.Sum(selector => selector.tic2.Microseconds);
var overall_result = overall_1 - overall_2;
Console.WriteLine($"overall analysis: {(overall_1 is < 0
? $"{nameof(bySetBlock)} was {Math.Abs(overall_result)} ms faster than {nameof(bySwitchBlock)}"
: $"{nameof(bySwitchBlock)} was {Math.Abs(overall_result)} ms faster than {nameof(bySetBlock)}"
)}");
}
public readonly ref struct CharblockExample
{
private readonly int inputtedLength;
private readonly bool _input = false;
private readonly char //char spots
#region Char-Spots
_0, _1, _2, _3, _4, _5,
_6, _7, _8, _9, _10;
#endregion
public CharblockExample(ReadOnlySpan<char> chars, bool performTest)
{
_input = true;
Span<char> arr = stackalloc char[MAX_LENGTH];
inputtedLength = chars.Length;
if (chars.Length > MAX_LENGTH)
throw new ArgumentOutOfRangeException(nameof(chars), $"""
The provided char array is longer than the max of 256 chars for this block.
""");
for (int i = 0; i < MAX_LENGTH; i++)
{
if (i >= chars.Length) break;
arr[i] = chars[i];
}
if (performTest)
{
int pos = 0;
ref var _ref = ref _1;
while (pos < MAX_LENGTH - 1)
{
switch (pos)
{
case 0: _ref = ref _0; break;
case 1: _ref = ref _1; break;
case 2: _ref = ref _2; break;
case 3: _ref = ref _3; break;
case 4: _ref = ref _4; break;
case 5: _ref = ref _5; break;
case 6: _ref = ref _6; break;
case 7: _ref = ref _7; break;
case 8: _ref = ref _8; break;
case 9: _ref = ref _9; break;
case 10: _ref = ref _10; break;
}
pos++;
_ref = arr[pos];
}
}
else
{
_0 = arr[0]; _1 = arr[1]; _2 = arr[2]; _3 = arr[3]; _4 = arr[4];
_5 = arr[5]; _6 = arr[6]; _7 = arr[7]; _8 = arr[8]; _9 = arr[9];
_10 = arr[10];
}
}
public char[] CharArr => new[]
{
_0, _1, _2, _3, _4, _5,
_6, _7, _8, _9, _10
};
private const int MAX_DISPLAYABLE = 15;
private const int MAX_LENGTH = 256;
}
это мой результат после полного выполнения:
общий анализ: bySwitchBlock был на 1240 мс быстрее, чем bySetBlock
@Sweeper, я собираюсь поискать в BenchmarkDotNet, это кажется действительно полезным. Можете ли вы опубликовать результаты комментариев в качестве ответа, чтобы я мог отметить как ответ?
Что вы подразумеваете под «текстовым типом значения»? С# не поддерживает строковые типы (это TypeScript)
Thread.Sleep(TimeSpan.FromSeconds(eachPause_s)) <-- Thread.Sleep очень неточен и использовать его здесь совершенно бессмысленно.
@Dai, это не поддерживает. Я пытаюсь реализовать это каким-то образом для хранения текста в стеке, что было бы действительно круто
rivate readonly char //char spots не имеет преимуществ перед полем fixed char[10].
@JamesJonatah «Я пытаюсь каким-то образом реализовать это для хранения текста в стеке» - вы не можете и не должны.
@Dai, не могу получить размер структуры с полем массива, что является обязательным, и это печально.





Тестирование CharblockExample из первой версии вашего вопроса с помощью BenchmarkDotNet дает результат, что использование switch примерно в 2 раза медленнее, чем его отсутствие.
BenchmarkDotNet v0.13.7, Windows 10 (обновление 10.0.19045.3086/22H2/2022)ЦП Intel Core i7-6700 3,40 ГГц (Skylake), 1 ЦП, 8 логических и 4 физических ядра
Пакет SDK для .NET 7.0.304
[Хост]: .NET 7.0.7 (7.0.723.27404), X64 RyuJIT AVX2
Задание по умолчанию: .NET 7.0.7 (7.0.723.27404), X64 RyuJIT AVX2
Метод Иметь в виду Ошибка стандартное отклонение Выключатель 363,3 нс 7,18 нс 11,18 нс NoSwitch 170,2 нс 3,44 нс 3,96 нс
Этого следовало ожидать. Вы выполняете много дополнительной работы — контролируете цикл, прыгаете с таблицей переходов и т. д. Я также не удивлюсь, если джиттер сможет распознать шаблон и оптимизировать серию назначений в коде без переключателя в что-то намного быстрее.
Вот эталонный код:
public class Benchmark
{
const string testText = $"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Quisque orci purus, vulputate vulputate mattis quis,
accumsan in turpis. Vestibulum quis sapien iaculis,
pulvinar magna sed, lacinia lectus. Donec nec felis nec
metus dictum aliquet vel non.
""";
[Benchmark]
public CharblockExample Switch() => new CharblockExample(testText, true);
[Benchmark]
public CharblockExample NoSwitch() => new CharblockExample(testText, false);
}
public class Program
{
public static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Benchmark>();
}
}
Как правило, такой код микробенчмаркинга дает довольно неточные результаты. На конечный результат может повлиять множество внешних факторов. Вам следует использовать подходящий инструмент для сравнительного анализа, такой как BenchmarkDotNet.
@AdrianHHH о боже. Спасибо за напоминание. Я понятия не имею, как я пропустил это.
Я протестировал структуру
CharblockExampleиз вашей первой версии с помощью BenchmarkDotNet. Использование переключателя примерно в 2 раза медленнее.