Some years ago I had a C# program that was taking too long to finish. The time required for it to finish processing was approaching a minute. For an interactive program, this was not acceptable, so I focused on optimizing the program.
Eventually I added multi-threading to the program, and it finished in less than 100 milliseconds. However, I was still curious as to whether other languages, like Go or Rust, could be faster than C#.
I ported the program to Go, and although I changed the algorithms a bit to be more efficient (this often happens when porting) the program was at least 20% faster, even without any optimization-specific work. The Go program took 80 milliseconds to finish.
Finally, I ported the Go program to Rust, and surprisingly this version was not only plagued with fewer bugs (it worked correctly right away), but it also was twice as fast as the Go version, at 40 milliseconds.
In the comparison of C#, Go and Rust, I found that C# and Go were similar, although Go was faster for command-line programs as it was compiled ahead-of-time. Rust meanwhile was the fastest programming language to use, and this was before any optimization work was done—it was just the first compiling version.
Even though it is used for many programs, VB.NET is mostly ignored even by its creators. Other languages like C# get much more attention; new features are often exclusive to C# in .NET updates.
But VB.NET persists, and I have found it has some good points—even features that C# does not have. One thing it does, for example is support case-insensitive syntax. So we can use "sub" in place of "Sub" for a subroutine.
This is helpful for VB.NET for these reasons:
It is a good compromise to just leave the syntax case-insensitive. This can also speed up development if you have a capitalization inconsistency but the program still compiles correctly. Case-insensitive syntax is a useful feature for VB.NET.
In the past I spent a significant amount of time working on large C# projects with many files. The C# language can be verbose and require excess time to create new classes—particularly several years ago.
One newer feature in C# that I feel can make developing large, multi-file programs easier is the "global using" directive. While "using" as a directive only applies to the current file, "global using" applies globally to all files in the project.
So each file that you create—like a new class file—you can omit the commonly-needed using directives. In many projects, a single directive is used in many places—like the System namespace for programs that write to the console, or System.Text for programs that use StringBuilder. I feel global using directives are a valuable time-saving (and file-size-saving) feature in this language.
For the most part, Go is a language where things are done in an obvious way. Slices, for example, are done on units of the original type, like elements in an array. But taking substrings is somewhat more complicated.
In Go, strings can be thought of as collections of bytes, and collections of characters (or runes). This is because some Unicode characters (like those with accents) have more than one byte. But ASCII strings have just one byte per character.
The Go substring article is one of my most useful:
Substrings in Go are done a little differently than in other languages like C#, but the idea of using slices for substrings makes sense. Writing the Go substring article helped me figure out this important aspect of Go.
Ruby is a language similar to Python but with object-oriented design, and an emphasis on syntax that makes programs easier to read and write. It is an enjoyable language to use, although I have not written any large or serious programs with it.
Ruby has traditional loops like for and while, but I find the iterator syntax, and methods like times, upto, downto, step and each to be more elegant. The iterators can even reduce bugs by avoiding the need for indexing variables.
Iterators have many diverse benefits:
Although the iteration block syntax is difficult at first, it makes sense the longer you use it. And many simple iterators do not require block syntax at all—we can just use the do-keyword instead. Having used Ruby in some projects, I find iterators to be one of Ruby's best features.
There are so many nice things about the Python language—it has a robust standard library, clear syntax, many libraries, and wide support—that I had to think for a while before writing this post.
Probably my favorite feature from Python is its syntax. The language use indentation to represent nested blocks instead of curly brackets, and it avoids semicolons to end lines. It looks simpler and cleaner than most other languages.
The syntax of Python probably has widespread effects:
In a sense Python's syntax makes it what it is—if it had more complex, expressive syntax, it would have been passed over as a "glue" language and would not have the same library support it has. Beginners would avoid such a language, making it less prevalent. And, like C++ or Rust, if Python had more complex syntax, it could probably be optimized to run faster.
I have mitigated a DDoS attack on this site that was causing a huge increase in bandwidth use over the last 13 days. I was worried I would go over the bandwidth limits in place on my server, which could be expensive.
The solution was to ban 3 ranges of IPs (with firewalld) that the attackers were using. The requests were obviously malicious and seemed to be aimed at crashing the server somehow. Caddy (my web server) did not crash, although it wasted a lot of CPU time servicing all these requests.
Here is what I found when I enabled logging in Caddy:
So the Internet has not been entirely consumed by DDoS attacks and AI companies using web crawling bots, but seems to be most of the way there.
The Go programming language has many uncommon features, like fast compile-times, multithreaded routines, and types that are safe to use on threads. But one of the first a new developer will encounter is multiple return values.
It makes sense to me that a method can return multiple values. I mean, it is 2025—we should not be limited to returning one value at a time. Other languages can return tuples, Options, Results, arrays or maps but Go has native support for multiple values.
There are some implications to Go's native multiple value support:
It's probably one of the earliest features a new Go developer will encounter, and it is so deeply-built into the language that multiple return values are one of the best features of Go.
C# is a relatively well-performing language—it is compiled and uses a type system that enables many optimizations. But sometimes good performance is not enough—the C# program needs to be as fast as possible. Probably the best C# optimization is to reduce or eliminate allocations.
In C#, every string is allocated upon the managed heap, a section of memory that allows variable-length data to be stored. By removing allocations of strings, we can save accesses to the managed heap, and also the later garbage collection passes that clean up the heap.
There are some other types that benefit from reducing allocations:
Keeping a cache of some sort and reusing these objects instead of allocating new ones has been, in my experience, the most effective C# optimization. You can see some examples on the array and Dictionary C# articles.
A lot of things have changed over the years I have been updating Dot Net Perls. These days, the Internet is almost a completely different place. I have been thinking about how I want to keep this site updated in the future.
I have encountered many issues recently with this website:
I believe the articles on this site still have value. I will keep maintaining them, but focus more on new writing. I am not sure if this blog is the path forward, but it will be tied to the existing code articles and maintained alongside them.