C
C#16mo ago
SWEETPONY

✅ How to create char[] faster?

I have following class:
[MemoryDiagnoser(true)]
public class Generator
{
[Benchmark]
public char[] StartGeneration1()
{
var random = new Random();

Span<char> str = stackalloc char[20];

for (var i = 0; i < 20; i++)
str[i] = ((char)(random.Next(26) + 'a'));

return str.ToArray();
}

[Benchmark]
public char[] StartGeneration2()
{
var random = new Random();

var str = new char[20];

for (var i = 0; i < 20; i++)
str[i] = (char)(random.Next(26) + 'a');

return str;
}
}
[MemoryDiagnoser(true)]
public class Generator
{
[Benchmark]
public char[] StartGeneration1()
{
var random = new Random();

Span<char> str = stackalloc char[20];

for (var i = 0; i < 20; i++)
str[i] = ((char)(random.Next(26) + 'a'));

return str.ToArray();
}

[Benchmark]
public char[] StartGeneration2()
{
var random = new Random();

var str = new char[20];

for (var i = 0; i < 20; i++)
str[i] = (char)(random.Next(26) + 'a');

return str;
}
}
cs I don't understand why but second version is a little bit slower: | Method | Mean | Error | StdDev | Gen0 | Allocated | |----------------- |---------:|--------:|--------:|-------:|----------:| | StartGeneration1 | 174.6 ns | 3.41 ns | 3.65 ns | 0.0143 | 136 B | | StartGeneration2 | 170.3 ns | 3.36 ns | 6.47 ns | 0.0143 | 136 B | why? is it possible to create char[] without allocating memory?
33 Replies
ero
ero16mo ago
the results say the second version is faster? and you can't allocate an array without allocating memory the array itself is the allocation
333fred
333fred16mo ago
The results say absolutely nothing You're within margin of error in either scenario
ero
ero16mo ago
i guess they're within error yeah
SWEETPONY
SWEETPONYOP16mo ago
but 174 > 170 so StartGeneration1 is slower
333fred
333fred16mo ago
No It's 174 + or - 3.4, and 170 + or - 3.36 When you look at your error, you need to consider whether there's any overlap In this case, you overlap between 171 (min for result 1) and 173 (max for result 2) Because there's this overlap, you can draw no conclusions from this test Run it again and you may get the exact opposite result
SWEETPONY
SWEETPONYOP16mo ago
I wanted to create random string as fast as possible so I decided to use stackalloc but maybe it is wrong
333fred
333fred16mo ago
And, frankly, I wouldn't expect there to be any statistically significant speed difference between these tests So that result is exactly what I would have expected you to get You are nearly certainly prematurely optimizing here
ero
ero16mo ago
it's probably faster if you get all the bytes and then cast to int or whatever that's assuming this was just for fun you wouldn't do that in actual code unless it's insanely performance critical
SWEETPONY
SWEETPONYOP16mo ago
it is only for fun
Aaron
Aaron16mo ago
if you want a string, then you should use string.Create
ero
ero16mo ago
sumpin like that
Aaron
Aaron16mo ago
SWEETPONY
SWEETPONYOP16mo ago
didn't know about that, thanks by the way! I found the fastest solution, one moment
public class Generator
{
[Benchmark]
public char[] StartGeneration1()
{
var random = new Random();

Span<char> str = stackalloc char[20];

for (var i = 0; i < 20; i++)
str[i] = ((char)(random.Next(26) + 'a'));

return str.ToArray();
}

[Benchmark]
public char[] StartGeneration2()
{
var random = new Random();

var str = new char[20];

for (var i = 0; i < 20; i++)
str[i] = (char)(random.Next(26) + 'a');

return str;
}

[Benchmark]
public unsafe char* StartGeneration3()
{
var random = new Random();

var array = stackalloc char[20];

for (var i = 0; i < 20; i++)
array[i] = (char)(random.Next(26) + 'a');

return array;
}
}
public class Generator
{
[Benchmark]
public char[] StartGeneration1()
{
var random = new Random();

Span<char> str = stackalloc char[20];

for (var i = 0; i < 20; i++)
str[i] = ((char)(random.Next(26) + 'a'));

return str.ToArray();
}

[Benchmark]
public char[] StartGeneration2()
{
var random = new Random();

var str = new char[20];

for (var i = 0; i < 20; i++)
str[i] = (char)(random.Next(26) + 'a');

return str;
}

[Benchmark]
public unsafe char* StartGeneration3()
{
var random = new Random();

var array = stackalloc char[20];

for (var i = 0; i < 20; i++)
array[i] = (char)(random.Next(26) + 'a');

return array;
}
}
third one is the fastest
Aaron
Aaron16mo ago
that doesn't work the third one returns a pointer to free'd memory
SWEETPONY
SWEETPONYOP16mo ago
ah..
Aaron
Aaron16mo ago
it was allocated in that method's stack frame, and that method's stack frame is gone after you return
333fred
333fred16mo ago
I would also love to see what output you got from the benchmark
SWEETPONY
SWEETPONYOP16mo ago
| Method | Mean | Error | StdDev | Gen0 | Allocated | |----------------- |---------:|--------:|--------:|-------:|----------:| | StartGeneration1 | 172.0 ns | 1.60 ns | 1.42 ns | 0.0143 | 136 B | | StartGeneration2 | 170.9 ns | 3.39 ns | 5.48 ns | 0.0143 | 136 B | | StartGeneration3 | 164.5 ns | 3.11 ns | 3.05 ns | 0.0076 | 72 B | // * Legends * Mean : Arithmetic mean of all measurements Error : Half of 99.9% confidence interval StdDev : Standard deviation of all measurements Gen0 : GC Generation 0 collects per 1000 operations Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B) 1 ns : 1 Nanosecond (0.000000001 sec)
333fred
333fred16mo ago
Gotcha. Still not statistically signficiant
Aaron
Aaron16mo ago
almost all the time here is spent doing Random.Next not in how you make and return the array
333fred
333fred16mo ago
Max of 3 is 167.61, min of 2 is 167.51 (There's also that point) (And also the fact that 3 returns a dangling pointer to invalid memory)
SWEETPONY
SWEETPONYOP16mo ago
hm ok we found a bottleneck! I will rewrite rnd.next
333fred
333fred16mo ago
You won't get faster I applaud the spirit of learning, but I really don't think this is it
ero
ero16mo ago
You can get a good bit faster
var bytes = new byte[length * sizeof(int)];
Random.Shared.NextBytes(bytes);
var ints = MemoryMarshal.Cast<byte, int>(bytes);
var bytes = new byte[length * sizeof(int)];
Random.Shared.NextBytes(bytes);
var ints = MemoryMarshal.Cast<byte, int>(bytes);
Something like that Probably better ways out there
Kouhai
Kouhai16mo ago
I assume they want a valid ascii char
SWEETPONY
SWEETPONYOP16mo ago
hm I thinked about this:
var buffer = new byte[20];
random.NextBytes(buffer);
var buffer = new byte[20];
random.NextBytes(buffer);
and then
for (var i = 0; i < 20; i++)
str[i] = (char)(buffer[i] % 26 + 'a');
for (var i = 0; i < 20; i++)
str[i] = (char)(buffer[i] % 26 + 'a');
and random is RandomNumberGenerator maybe I will test this harold
Aaron
Aaron16mo ago
I believe RandomNumberGenerator is slower than Random is since it's cryptographic randomness instead of a prng
SWEETPONY
SWEETPONYOP16mo ago
SWEETPONY
SWEETPONYOP16mo ago
but maybe second one is wrong, I will test the output works fine thanks guys
jcotton42
jcotton4216mo ago
@ivefifthsence use string.Create
Tarcisio
Tarcisio16mo ago
how can i learn to be smart like you
Denis
Denis16mo ago
Work hard
GrabYourPitchforks
The second one introduces a slight bias toward earlier letters of the alphabet. For example, A will appear in the output more than Z, and this will be statistically significant.
Want results from more Discord servers?
Add your server