β id love to get a code review if possible
this is a segmented sieve code I wrote. it is meant to be as fast as I can get it, however id mostly like to get coding conventions for cs down
162 Replies
couldn't fit the main, here it is:
your naming conventions aren't what are normally used in C#
you'll generally want to avoid stateful static members
new List<int>(new int[] { 2, 3, 5, 7 });
technically wastes an array allocation though it's basically irrelevant for performance heremind elaborating on the second point? meanwhile ill look into the naming conventions?
static members aren't associated with any particular instance of an object which can make them hard to test and clean up
for a little program like this it's fine, but maintainability can become a problem
I was trying to make exactly that, I only want one prime list in the class and no instantiation
no constructor in the class
would this name "addNextSegment" be the expected name?
methods are always PascalCase, that's one thing that i basically never see people put different spins on
local variables are camelCase, class fields are also usually camelCase but some people (like me) do _camelCase
i think MS has their own code style recommendations somewhere
C# Coding Conventions
Learn about coding conventions in C#. Coding conventions create a consistent look to the code and facilitate copying, changing, and maintaining the code.
ty
also how would you do this?
in rust id do
but im not sure if there is a list macro in C#
you can just do
new List<int> { 2, 3, 5, 7 };
oh lol, ty
also there are no kinds of macros in C#
oh, so if I want something like a macro I have to do it in runtime?
to an extent
wait C/C++ has macros isn't C# a superset of C++?
not at all
no pointers, for example (unless you really want them)
memory is generally managed for you
also everything is an object or a member of an object type, you can't have standalone functions
the syntax is similar, that's about it
nice, thanks a lot you helped me a ton π
as for macros/templates/etc, they don't exist but there are ways you can do compile-time code generation
e.g. source generators
reminds me of python a bit
if you've touched java C# is essentially java but better
yea I figured there is a way, but it's prob a bit more advanced then what I know in C#
I heard the joke of microsoft java lol
unless i'm wrong it literally was microsoft java because they didn't want to deal with sun/oracle or something
i should correct myself about no templates, c# has generic types which are similar but significantly more limited
I see ty
for example
List<int>
is a generic List<T>
specialized to hold int
objectsyea, same as in rust
oh, in here
hi here im potzko
in, not i'm :p
oof lol
I made
public class Primes
but no constructor
so only one class
with no instances I think
technically no
you can make as many instances if your class as you want because it isn't marked
static
just do
public static class Primes
it will stop you from ever making an instancebut the only state you have is static, so instances wouldn't have anything "in" them
IMO it would still be better to make it non-static and pass the same instance around
oh so because I wrote all of the data is static, it is all saved to the class, and each instance will not have any data
right, static fields have state independent of any instance of the class
You might want to check out C# naming convention as well.
so I can make 20 instances and they will all "point" to the same prime list
not even that
yea I already changed it
they aren't accessible on an instance of the type, only the type itself
because the data is not public by default right?
that as well
no, its just because you aren't allowed to do static things with an instance
its like if you did
Ah missed it.
i don't have enough thigh-highs for this conversation anymore
ah I see
lol
Also consider
var
instead of explicit types:
they said rust, so i just used the language i know they know
oh that's nice, like let π
i'd personally avoid var until you're more familiar with the .NET BCL and generally know what types exist
seen some people make mistakes because something was a type they didn't expect
they were total beginners though, not coming from another language
I doubt for them, Rust uses type inference heavily.
I think ill hardcode types untill I can write stuff on notepad that compiles
then var all the way
same as I did in rust
also, might be personal preference but
int ind = seg_start + (prime_num - (seg_start%prime_num))%prime_num
is entirely too much to put inline with a for loopis this better or worse?
worse
On performance side of things, I would say a simple low hanging fruit would be to give a good initial size to
primes
; if you know you are calculating for 10 million primes, set its size as so, so it won't be resizing all the time.i'd declare another variable before the loop with the calculation then just assign that to
ind
or declare ind
outside the loop and skip initializing itprob in getNthPrime right? im not sure how to do that so I let the runtime figure things out
like this?
i still don't really like skipping bits of the for loop but breaking it up is going in the right direction imo
if i were to nitpick i'd want spacing between each operator as well
Lists aren't magic, it's backed by some underlying memory; when you keep adding elements to it that the underlying memory cannot fit anymore, new chunk of memory has to be allocated and move the old stuffs over.
If you know ahead of time how many elements there will be, then you having that size would prevent constant resizing and copying.
I like doing it based on order of operations if there are only 2 groupings, +- get a space, */ don't or +- get a space and brackets do not, depending on the line
it's ultimately personal preference, i just wouldn't let you do it in my codebase π
yea, in rust I used .reserve I just couldn't find the equivalent in the List documentation in Microsoft's website
EnsureCapacity
you can also use the constructor with capacity
well, not its not reserve, its resize
new List<int>(initialCapacity) { elements }
ty, is that added, or up to?
EnsureCapacity(3) -> I have 3 bytes, or I have 3 more bytes?
exactly 3
or the current list count
elements, not bytes
whichever is greater
ty
I think it matters more for stuff you are not sure about, A | B & C for example, in my opinion is less clear then A | B&C
huh, i never realized how many weird unsafe methods are on Vec
spare_capacity_mut
is scary i just use more parentheses until it makes sense
il get lisp flashbacks
i mean, i also don't do a crazy amount of math on one line either
just add a line for each compare, and for each line say exactly what you do smh
i'll break it into meaningfully named variables so i don't come back a week later and wonder WTF this equation is doing
Id also do it, I think that for 3-4 ops where order of operations is relevant I like to see it in code
and I don't want too many parans everywhere as it makes it harder to skim through imo
I updated the code I think it looks a bit weird but it might be because im used to the camel case
second piece of code is a little misleading
yea I keeping the progress
might add a remove primes function to the start of test
i mean
main
isn't actually the entry point
i forget MS's term for the program class without all the boilerplateC# console app template changes in .NET 6+ - .NET
The .NET 6+ project template for C# console apps uses top-level statements. Understand what changed and how to use existing learning materials with the new syntax.
$toplevelstatements
$toplevelcode
$toplevelcode
Traditionally in C#, the main entry point of any program would be a static
Main
method (see $mains): In C# 9, a feature called top-level statements was added which allows the static Main
method to be omitted in favor of brevity and simplicity, and in C# 10, this was made part of the default console application template:
If you are following a tutorial or example code and your default template looks different, you can still write the exact same code as if you were writing it inside the static Main
method. In addition, you can still write static methods inside top-level code. You can however not use top-level code in more than one file.
If you wish to use the old traditional entry point, you can still use a Program
class with a static Main
method.
https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/top-level-statementsTop-level statements - programs without Main methods
Learn about top-level statements in C# 9 and later. You can create programs without the ceremony of a Program class and a Main method.
hate it, why do we have so many tags for top level statements we have $tls too
$toplevelcode
why does one tag just tell you to use the other tag
my god, that hello world has 13 words in it
so people only have to remember to update one tag
but you also can't get confused about what the tag name is and get it wrong
I think I can unironicly beat it in asm
you can take a lot of words out tbf
if you really want to
when I did it I just wrote Console.WriteLine("hi mom"); and it worked
can go down to 7 or 8
yeah, that's the top level statements feature
you're still in the
Program.Main
method, it just generates all that for you by magicβ’οΈautomagicly oriented programing
as a side note, that means any functions you define in a top-level statements file are local functions
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/local-functions
are there compiler optimization levels? or is it always maxed
just debug and release
default is debug,
dotnet run -c Release
for releaseoptimization is runtime JIT magic the JIT will progressively optimize code paths if it thinks they're hot enough
which is also why you shouldn't benchmark by just running a stopwatch around your method
also how bad of an idea is adding this on the last lines of addNextSegment?
don't call the GC yourself
not really a reason to
but yeah, can highly recommend https://benchmarkdotnet.org/
here I thought C# was just an IL compiler but apparently it's a jit lol
well something has to turn the IL into machine code
il check this out soon
well, we have to run that IL somehow
options are either interpreter or JIT
and one of those options is
can I see the IL output?
yeah
with a decompiler or built in IDE tools
GitHub
GitHub - icsharpcode/ILSpy: .NET Decompiler with support for PDB ge...
.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform! - GitHub - icsharpcode/ILSpy: .NET Decompiler with support for PDB generation, ReadyToRun, Me...
is there no -S thing?
or ildasm, which comes with the sdk
but uhhh
ildasm is a pain
there's no way to get the compiler to show it to you, no
you just have to disassemble the final dll
rider has a built in IL viewer, i'd be surprised if VS doesn't have something similar
you can make it do lowered C# too
i don't think it does, actually
it has a decompiler, never seen an IL disassembler
you guys were super helpfull, I think ill write another thing and call it a day, thenks a lot π
@potzko this is what it looks like btw
getting the 50 millionth prime takes about 1.7 seconds
I think it's keeping the progress for each run
its not
i made it not do that
that includes allocating the list, even
something does not look right as it should take when written in C or rust around 3-4 seconds
oh wait, maybe your computer is just much better
ryzen 9 5900x
:p
maybe its only deallocating the ram after it's done with the test?
1.7 seconds is insanely fast
i mean, it only allocates 1.5gb per test
and it can reuse that space, since the other lists disappear after its done
when you get to making objects this large the GC basically just does malloc and free anyway
it takes 1.7 seconds with 17 seconds of error and a standard deviation of 16, something smells fishy
thats not 17 seconds of error
thats 17 milliseconds of error
oh lol
welp, blazingly fast I guess
π
i can run the rust counterpart to compare
rust will probably still be a bit faster
one sec ill get it
lets see if i can remember how the hell criterion works
didn't use criterion on it I think
had to cut documentation to fit it in here lol
a hair faster
your computer is fast
2700x
on mine it takes 5 seconds
~1.63s without lto
(forgot i had it on in this project)
what is lto?
ah that thing
basically just means it made optimizations between std and the running crate, in this case
yea
i just noticed that get returns a &usize here, lmao
im lazy
also I can't get your benchmark to run, I did dotnet add package BenchmarkDotNet added some using statements and then dotnet run -c Release and it's saying there is no main
am I missing something?
yeah
ah I see ty
didnt send it earlier since it was in a different place in the file
you can also get rid of
unsafe
there, lmao
this project has a ton of random shit in it everywhere since its my scratchpadI was scared to touch your code so I just added <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
$allowunsafe
lmao
your computer is fast lol
Β―\_(γ)_/Β―
whats your CPU
i5-8400
but I think it's more of a ram bottleneck
when I threaded the rust code I got almost no speedup
i'm not sure this can be threaded very well anyway
you need to know all previous primes, don't you?
up to root, so after 2 iterations you are free to thread 4 cores forever
ah
or within the function you can thread this loop
fair enough
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.