Tag Archives: programming-language

Go vs. C++, First Glance

Adapting to Go from C++ over the last couple of months hasn’t been easy. I was told by many that compared to other mainstream programming languages, C++ is the closest to Go. Well, I’m not sure where that came from, but it’s not true.

I decided to write an article comparing both languages at first glance. This won’t be a super detailed article, as I’m going to write on a lot of topics.

1. Introduction

C++, developed in the 80s as a superset of C, is a statically typed and compiled language. It is highly efficient, customizable, and portable, making it ubiquitous across virtually any industry. Its exceptional performance has established its dominance in the gaming, embedded systems, operating systems, and scientific research industries. As a superset of C, it introduced features such as object-oriented programming, templates, and exception handling. However, as the language ages and continues to add more features to support various fields, its highly customizable nature and verbose syntax are making it increasingly difficult to learn. Its package and third-party library management are decentralized, unlike Go or Python, making managing third-party libraries a real pain for C++ developers. Despite tools like Conan, it remains extremely challenging to establish centralized package management, as libraries are spread out and there isn’t an official package manager for C++.

Go, a language developed by folks at Google out of a shared dislike for C++, is designed to be statically typed and high-performance like C++, but also highly readable like Python. Its simplicity and performance have gained tremendous popularity over the years. Compared to C++, Go offers memory safety through its built-in garbage collector, features a centralized package management system, and compiles significantly faster. Its CSP-style concurrency model ensures minimal deadlocks and supports lightweight threads. This, coupled with strong performance on multi-core systems and in networking, has established Go’s dominance in the web-backend and microservices industries.

2. High-Performance Computing Comparison

Concurrency Handling

Unlike C/C++ style multi-threading that requires explicit locking and unlocking, Go implements CSP-style concurrency with goroutines and channels. In Go, concurrent units communicate data through channels, which can operate synchronously (unbuffered) or asynchronously (buffered), functioning similarly to a queue. This model is generally safer and less prone to deadlocks compared to traditional multi-threading approaches. Additionally, Go’s concurrent units are lighter than traditional OS-managed threads in C/C++, as they are managed and scheduled by the Go runtime.

Due to Go’s efficient use of multi-core CPUs and memory, it has an advantage in vertical scaling, making it a better choice for services running on a single machine. While C/C++ offers more customization and fine-grained control due to its native support for OS-level thread management, it often performs better in distributed system environments.

There is no clear winner between the two; Go also supports distributed computing with robust libraries, and C/C++ can perform exceptionally well on single machines due to the low overhead of OS-managed threads. The choice often depends on specific application requirements and the environment.

Memory Management

Fundamentally, like C++, Go uses the stack for variables and function calling, while larger and dynamically allocated data is handled on the heap. A unique aspect of Go is its goroutine management: a goroutine starts with a small allocation on the stack and gradually increases its size as needed. If the stack grows too large, it is relocated to the heap to accommodate further expansion. The key differentiator in memory management between Go and C++ is Go’s garbage collector (GC).

C++ does not include a built-in garbage collector. Instead, it utilizes smart pointers such as unique_ptr and shared_ptr from the Standard Template Library (STL). These smart pointers automatically manage memory by deallocating it when no more references to the object exist (reference count reaches zero).

Go’s garbage collection, on the other hand, runs concurrently with the application and employs a mark-and-sweep algorithm. This algorithm can be further explored in the Go garbage collection guide (https://tip.golang.org/doc/gc-guide). Briefly, the mark-and-sweep algorithm works in two main phases: the mark phase, where the garbage collector traverses the object graph starting from root pointers to mark live objects, and the sweep phase, where it reclaims the memory occupied by unmarked (unused) objects. This type of GC is non-moving, meaning it does not relocate live objects as part of the collection process, which helps in minimizing pause times. The concurrent nature of Go’s GC is particularly beneficial for real-time applications since it allows the program to continue executing with minimal interruption during the garbage collection process.

In comparison, Java’s garbage collector is more complex and includes several algorithms such as generational, parallel, and concurrent mark-sweep, as well as moving GCs like G1 and Shenandoah, which perform object compaction to manage heap fragmentation and improve memory efficiency.

Compiler Optimization

When it comes to compiler optimization, C++ takes it to the extreme. Known for its extensive compiler optimizations, compilers like Clang and GCC offer multiple optimization levels. Each level further enhances performance, progressively reducing execution times. In contrast, Go’s compiler focuses on simplicity and the speed of compilation. In terms of optimization, Go performs fewer optimizations; techniques such as loop unrolling and branch prediction hints are not commonly utilized by the Go compiler.

The philosophical design differences between Go and C++ result in varying complexities in their dependency management. Consequently, Go does not spend as much time in the linking phase as C++. Moreover, as noted, Go’s compiler does not delve into sophisticated optimizations.

3. Libraries and Ecosystem

When it comes to third-party libraries and ecosystems, C++ has a more mature ecosystem as it is much older than Go. C++’s third-party libraries provide robust and complete support from embedded systems to GUIs, and they are highly customizable in terms of integration with existing programs. However, a significant downside is that C++ does not have a centralized package management tool, as mentioned before. Although developers can use tools like Conan or vcpkg, they do not support all third-party libraries out there.

On the other hand, Go provides a package management tool, go get, which makes the process of integration simpler and faster, allowing developers to spend less time managing dependencies. But an obvious downside is that Go’s libraries are less mature and offer less control over how they integrate with existing programs.

I remember having to spend hours or even days configuring the dependency hell I was stuck in when writing a complex C++ program; all those CMakeLists files and flags really got me confused. However, each library is very fine-tuned for optimization and extremely powerful once integrated and ready to use. While learning Go, I realized that Go’s libraries are standardized, but they offer less freedom code-wise. This is a trade-off Go made, stemming from its design philosophy.

4. Syntactic Sugar Comparison

The syntactic differences between C++ and Go greatly reflect their design philosophies. C++ focuses on customizability and performance, while Go prioritizes simplicity and readability.

C++ allows developers to overload operators, define templates for generic programming, employ RAII, and utilize low-level functions to mitigate overhead. In contrast, Go supports type inference and short variable declarations (:=), the range clause, and the defer statement. These features enable Go developers to enjoy shorter development cycles and easier troubleshooting but add more overhead under the hood. Meanwhile, C++’s high customizability provides developers with flexibility and enhances program performance. However, its inherent complexity can make it difficult to troubleshoot and maintain.

5. Conclusion

In conclusion, C++ and Go are very different in many ways, they each have specific fitting industry without too much overlap. They represent the evolvement and development of programming language over the years, reflecting the needs and preference in the software engineering industry. Learning the differences between these two languages from a developer point of view allows me to extinguish the desire and goals between different softwares and industry, gaining a more insight on the programming language choices for various problems.

As someone who has been developing in C++ for years, I have written games, file system, embedded softwares, and web servers, C++ has always been a huge part of my software developement career, every new languages I learn, Java, Python, Lisp, Javascript, they give a entirely new insight on the industry that they primarily use for, including Go. Coding Go for microservice architecture gives me the chance to understand the modern tech industry solution for large user base software and multi-purpose server.

This journey through different programming languages not only enhances my technical repertoire but also prepares me for future challenges and innovations in the fast-evolving tech landscape.