
Is C# a low-level language? - benaadams
https://mattwarren.org/2019/03/01/Is-CSharp-a-low-level-language/
======
MarkSweep
If you really wanted to, you can program in a subset of C# that is similar to
C. You can avoid the garbage collector by using a native memory allocator and
then manipulate pointers with the usual * and -> operators.

While you probably don't want to write a whole program like that, sometimes it
is helpful to use these features when porting code from C or for performance
reasons. See for example this implementation of memmove that is used for
copying strings and other things:

[https://github.com/dotnet/coreclr/blob/7ddd038a33977b152e856...](https://github.com/dotnet/coreclr/blob/7ddd038a33977b152e856448443425cbc6b591c6/src/System.Private.CoreLib/src/System/Buffer.cs#L183)

~~~
JustSomeNobody
This is basically what I do. We don't have real time constraints, but we do
have a limit to how long processing can take so we need to shave as many ms
off how long our code executes as possible, so we pretty much use just the
bare minimum features of the language and profile the crap out of everything.

One could argue that C# wasn't the best choice, but that was out of my hands.

~~~
voltagex_
Are you able to share any examples of functions where writing C# like that
gave measurable benefits? I'm over in web-land and it's rare that C# itself is
the bottleneck.

~~~
scott00
One example from my line of work that could conceivably translate to web-land
would be parsing numbers. I wrote a version of double.Parse() with less
features for an order of magnitude speedup. The program was doing data
analysis on a bunch of CSV files, and just parsing the files was way slower
than EBS I/O.

------
slaymaker1907
For those saying a GC is too high level, malloc is also a high level
abstraction. There are relatively simple implementations of malloc, but
allocators like jemalloc are starting to look quite a lot like a GC in the
ways they manage memory.

The real issue with GCs is the lack of control that makes it very difficult if
not impossible to do things like grouping allocations together. However, this
capability comes at a cost since then you can’t move around existing
allocations without going to extreme lengths and can easily lead to memory
fragmentation.

------
int_19h
This conflates the language, a particular implementation of that language, and
a particular runtime that said implementation uses.

As far as the language goes, you can do anything in C# that you can do in C in
terms of memory-unsafe programming - raw pointers, pointer arithmetic, unions,
allocating data on the stack - it's all there, and it's not any harder to use.
Since semantics are the same, it should be possible to implement it just as
efficiently as in C. At that point, the only overhead that you can't remove by
avoiding some language feature or another is GC, although you can avoid making
any allocations through it. So the real question is whether a language with GC
can be considered low-level.

------
booleandilemma
C# is managed code, so no, it’s not low-level by any reasonable definition of
the term and calling it so would be confusing to anyone getting started in
this stuff.

~~~
BinaryIdiot
C# _targeting the .Net runtime_ is managed code. There are subsets, including
Unity's new HPC#, that compile directly to assembly.

I think it's important to separate the language from the runtime so people
come out less confused versus conflating them to cover the 90% use case.

~~~
reitzensteinm
Pedantically asserting that it's possible to use a subset of a language and
feed it through a special compiler to write low level code is _not_ the way to
make it so people are _less_ confused.

We live in an era where you can boot Linux in JavaScript. If every time you
make a statement about computing you have to list the exceptions, that's
sinking in to the Turing tarpit for documentation.

~~~
BinaryIdiot
Normally I'd agree with you, especially when concerned with languages like
JavaScript. But C# is a different beast. It's being used quite frequently in
large gaming projects and even operating systems where it is frequently less
managed.You can even use it out of the box in a more, but obviously not
entirely, unmanaged way to increase performance.

I don't consider this pedantic in this case. But the line isn't hard so we'll
have some disagreement how to teach / not teach this.

~~~
reitzensteinm
Do you have any examples of projects where it's used in a "less managed"
context? Are you just talking about avoiding GC, or something more advanced?

Even when you write in a low level style using the .Net/Mono VM, you're still
fully protected by the guarantees of the language and runtime. You can't chase
bad pointers around through memory or overflow the stack. It's impossible to
access an array without a bounds check unless the JIT can prove it's not
needed. The safety this provides is night and day to what it's like coding
close to the metal (I develop games in C# and have written a fair amount of
C/asm).

But as you say, we can agree to disagree about that...

~~~
pjmlp
> You can't chase bad pointers around through memory or overflow the stack.
> It's impossible to access an array without a bounds check unless the JIT can
> prove it's not needed.

Many native languages have these features without having a managed runtime.

------
mcraiha
Unity is also going to do some C++ -> C# conversions
[https://blogs.unity3d.com/2019/02/26/on-dots-
c-c/](https://blogs.unity3d.com/2019/02/26/on-dots-c-c/)

~~~
hesdeadjim
Unity has gone far beyond cross compilation of C# to C++ now. Their Burst
compiler is capable of compiling C# straight to assembly with vectorization
optimizations. They’ve even ported core C++ components of the engine into
bursted C# because the resulting code is faster and a quarter of the size.
It’s impressive stuff.

~~~
fendy3002
I wonder why they choose c# over other language common for scripting / gaming
engine, like lua c++ or even java. I think C# is good and designed well (over
java), and I wonder if it's the same for them.

~~~
mycall
Unity features three scripting languages; JavaScript/UnityScript, C#, and Boo

~~~
MattRix
This is technically true, but these days using anything other than C# is
strongly discouraged.

------
fallingfrog
I remember back when they called C a high level language- because it wasn’t
assembler. Yes, I’m old. But, my point is that there probably isn’t a
definitive answer to that question that won’t change with changing technology.

~~~
zoom6628
Speaking as another old person (50+) yes i too remember when C started
becoming in-vogue for apps (shortly before VB4) and it was considered high-
level by a lot of folks. Even in a COBOL enterprise system in which i worked
at the time C was considered more high-level than low-level although we used
it to build a runtime for COBOL apps. I have seen this type of discussion go
on for decades, literally, but to my simply mind low-level means at the
machine instruction set level. Everything above that is something else and i
think the label of high or low level is frankly both arbitrary and
meaningless. In recent years having spent time working with C on Arduino and
Python for apps/hacks, and I also have dabbled with pascal(several variants of
it), the only term i can think of to meaningfully describe any language is
'fit for purpose'. And if your purpose is to do ray-tracing from an
environment that needs be supportive of programming noobs then maybe C# with
tweaks is the best fit. If your environment requires burning programs directly
into silicon then maybe verilog is best fit for purpose.

Its not about what you like, what you know, what is trendy, what is top of the
pops this week - its about getting shit done and that happens when you use the
right tools. The tech industry, like every other industry, ultimately involves
people and people are as subject to fashion, peeves, and emotional attachment
to their investments(i.e. the languages you know well) - a comment on C# vs
C++ seems to start wars just as religious as vi vs emacs as we had back in the
80s with COBOL vs RPG3!

End of rant.

------
0xTJ
Some people say that C isn't low-level (though I disagree). I personally don't
think of C# as even remotely low-level.

~~~
Posibyte
The author takes this into consideration:

    
    
      * yes, I know ‘low-level’ is a subjective term 
    

I used to work in the embedded world, and there C has historically been
considered a "high-level" language for many platforms. However, we have
started to see languages like C# and Python make their way into the world of
tiny micros thanks to more power coming to the platforms themselves and the
clever work being done by MicroPython and Microsoft.

~~~
htfy96
The problem is that most legacy code (especially firmwares) is C and will
never have a chance to be rewritten in another language. If you get a chance
into VLSI field you will be amazed that they are still relying on Tcl to do
complicated stuffs. Only part of the world's software (most in IT companies)
can afford rewriting in a new language regardless of costs/upstreams.

------
mooman219
This has a click-baity title. A mostly math/calculation heavy C++ program is
translated into a mostly math/calculation heavy C# program. You could replace
either side with any language and get the same outcome. At best, the end of
the article lists some hacks you _could_ write in C# to get what you consider
"low-level", but ultimately defeat the purpose of using a "high-level"
language in the first place. If you're using unsafe calls and platform
intrinsics, it's more than a one off situation, and the language doesn't
encourage it, then maybe you're trying to hammer a nail with a shoe.

~~~
guu
If they replaced C# with a language like ruby I suspect it would not get
within the same order of magnitude of performance.

I agree that it may not be the best choice, but it is still interesting to see
what is possible.

~~~
EpicEng
Sure, but I could write a computer for some new flavor of assembly tonight.
The output would not by performant, but the language would certainly be low
level.

------
jedimastert
Slightly to the side of the topic, but this quote here:

>Yep, Bob uses i++ instead of ++i everywhere. Meh, what’s the difference.

I know what the difference is, but if you're just using it on its own line or
in a for loop, why would you use `++i` instead of `i++`?

~~~
eMSF
Why didn't you ask the question the other way around? :-)

For some inexplicable reason the postfix operators are usually taught and
learnt first, and thus considered more natural, even though there are more
objective reasons for the opposite, at least in C++ world.

~~~
derefr
> For some inexplicable reason the postfix operators are usually taught and
> learnt first, and thus considered more natural

The (unary) postfix operators might have _semantics_ that are more surprising
than the prefix variants, but the postfix _syntax_ itself feels a bit more
natural and fitting with other syntax of the Algol language family, IMHO, when
compared to the prefix syntax. I think that's what leads people to introduce
it first.

You can sort of read `i++` as an "abbreviation" of `i += 1`—in both cases,
you've got an operator that follows an lvalue variable, and which both reads
from and mutates the memory referred to by that variable. The only difference
is that one has an additional "argument"—the amount to mutate the lvalue _by_
—while the other has a "default" argument of +1.

`++i`, on the other hand, is a _surprising_ bit of syntax, the first time you
see it. It takes (and mutates) an lvalue... on its right! None of the (few)
other unary prefix operators in Algol-like languages take lvalues, so it's
kind of a shock.

(Also, for newcomers to programming, unary - [numeric negation] is commonly
interpreted as part of the grammar of a number literal rather than being an
expression; and unary ! is commonly seen as just "part of the language" (i.e.
less like & and |; more like && and ||). Unary ~ is the only one I'd expect it
would occur to any new programmer as even _being_ a "unary prefix operator"
rather than just "the way the language is." And it's a pure-functional
transformation of its input.)

------
neokantian
As soon as you default to a particular policy of how to allocate, and most
importantly free, memory on the heap, you have committed yourself. Low level
means: no commitment in that realm, aka, do what you please. The fact that you
can possibly overrule that default, does not make much of a difference,
because almost any language allows this.

C does not even have a standard (dynamically-sized) list concept built in,
because that would amount to committing oneself to a default heap
allocation/deallocation policy. All you can get is a contiguous range of bytes
with some vague, default interpretation as to what it is supposed to be,
through the use of malloc/free (possibly hacking it through realloc). That is
why C is considered low level.

Still, in a true low-level solution, you would use the sbrk system call
directly. So, in a sense, C may already "add too much value" to be considered
truly low level.

~~~
hossbeast
You can easily can sbrk (or mmap) from C directly.

~~~
neokantian
Yes, exactly. You can even do that from any arbitrary scripting engine that
supports executing arbitrary C functions.

For example, here, complete access to the linux/bsd/osx system call interface,
including sbrk, from lua:
[https://github.com/justincormack/ljsyscall](https://github.com/justincormack/ljsyscall)

Just load libffi
([https://sourceware.org/libffi](https://sourceware.org/libffi)) or a similar
module in any arbitrary language, and off you go!

------
mattnewport
Why was the conditional expression in the C++ code converted to an if
statement in the C# code? C# has conditional expressions.

~~~
ken
It's been a while since I've used C#, but I'd guess it's because the C++ is
doing a "+=" with ",", to cram two statements in there, while C# considers
"+=" a toplevel statement only and doesn't allow it to be used with ",".

------
manigandham
It's subjective but recent language and runtime updates have made massive
updates in performance and flexibility. There are projects like Unity, RavenDB
document store, FASTER KV store, Trill analytics, ML.NET and many more that
show that C# is incredibly capable of competing at low-level/high-performance
scenarios.

------
walkingolof
I would say no, but its all about lingo, low level languages, or system
languages, can be used to bootstrap a computer.

------
samfisher83
Can't you just do pinvoke. It is easy enough to do. Just design a good
interface in the c side to pass the data.

------
firethief
Are trees tall?

