TNS
VOXPOP
Where are you using WebAssembly?
Wasm promises to let developers build once and run anywhere. Are you using it yet?
At work, for production apps
0%
At work, but not for production apps
0%
I don’t use WebAssembly but expect to when the technology matures
0%
I have no plans to use WebAssembly
0%
No plans and I get mad whenever I see the buzzword
0%
Software Development / WebAssembly

WebAssembly and Go: A Guide to Getting Started (Part 2)

By combining WebAssembly and Go, developers can achieve exceptional performance in web applications. Check out Part Two of our series on Golang and Wasm to learn more.
Jun 12th, 2023 5:00am by
Featued image for: WebAssembly and Go: A Guide to Getting Started (Part 2)
Image by Moritz Kindler from Unsplash. 
Editor’s note: This is Part Two of a two-part series on WebAssembly and Go. You can find Part One here.

WebAssembly (Wasm) and Golang (Go) are a dynamic duo for high-performance web applications due to their specific features and advantages. Wasm is a binary instruction format that allows running code at near-native speed in modern web browsers. It provides a low-level virtual machine that enables efficient execution of code, making it ideal for performance-intensive tasks.

Go is a statically typed, compiled programming language known for its simplicity, efficiency and high-performance characteristics. It offers built-in concurrency support, efficient memory management, and excellent execution speed. These qualities make Go a suitable language for developing backend systems that power web applications.

By combining WebAssembly and Go, developers can achieve exceptional performance in web applications. Go can be used to write backend services, APIs and business logic, while WebAssembly can be used to execute performance-critical code in the browser. This combination allows for offloading computation to the client-side, reducing server load and improving responsiveness.

Furthermore, Go has excellent interoperability with WebAssembly, allowing seamless integration between the two. Developers can compile Go code to WebAssembly modules, which can be executed in the browser alongside JavaScript, enabling the utilization of Go’s performance benefits on the client side.

Performance is of paramount importance in web applications for several reasons:

User experience. A fast and responsive web application enhances the user experience and satisfaction. Users expect web pages to load quickly and respond promptly to their interactions. Slow and sluggish applications can lead to frustration, abandonment and loss of users.

Conversion rates. Performance directly impacts conversion rates, especially in e-commerce and online businesses. Even minor delays in page load times can result in higher bounce rates and lower conversion rates, studies have shown. Improved performance can lead to increased engagement, longer session durations and higher conversion rates.

Search Engine Optimization (SEO). Search engines, like Google, take website performance into account when ranking search results. Faster-loading websites tend to have better search engine rankings, which can significantly impact organic traffic and visibility.

Mobile users. With the increasing use of mobile devices, performance becomes even more critical. Mobile networks can be slower and less reliable than fixed-line connections. Optimizing web application performance ensures a smooth experience for mobile users, leading to better engagement and retention.

Competitiveness. In today’s highly competitive digital landscape, performance can be a key differentiator. Users have numerous options available, and if your application is slow, they may switch to a competitor offering a faster and more efficient experience.

How Wasm Enhances Web Application Performance

Near-native performance. WebAssembly is designed to execute code at near-native speed. It achieves this by using a compact binary format that can be efficiently decoded and executed by modern web browsers. Unlike traditional web technologies like JavaScript, which are interpreted at runtime, Wasm code is compiled ahead of time and can be executed directly by the browser’s virtual machine, resulting in faster execution times.

Efficient execution. WebAssembly provides a low-level virtual machine that allows for efficient execution of code. It uses a stack-based architecture that minimizes the overhead associated with memory access and function calls. Additionally, WebAssembly operates on a compact binary format, reducing the size of the transmitted code and improving load times.

Multilanguage support. WebAssembly is designed to be language-agnostic, which means it can be used with a wide range of programming languages. This allows developers to leverage the performance benefits of Wasm while using their preferred programming language. By compiling code from languages like C, C++, Rust, and Go to WebAssembly, developers can take advantage of their performance characteristics and seamlessly integrate them into web applications.

Offloading computation. Wasm enables offloading computationally intensive tasks from the server to the client side. By moving certain operations to the browser, web applications can reduce the load on the server, distribute computation across multiple devices and improve overall responsiveness. This can be particularly beneficial for applications that involve complex calculations, image processing, simulations and other performance-critical tasks.

Seamless integration with JavaScript. WebAssembly can easily integrate with JavaScript, the traditional language of the web. This allows developers to combine the performance benefits of Wasm with the rich ecosystem of JavaScript libraries and frameworks. WebAssembly modules can be imported and exported from JavaScript code, enabling interoperability and smooth interaction between the two.

Progressive enhancement. Wasm supports a progressive enhancement approach to web development. Developers can choose to compile performance-critical parts of their application to WebAssembly while keeping the rest of the code in JavaScript. This way, the performance gains are selectively applied where they are most needed, without requiring a complete rewrite of the entire application.

WebAssembly vs. Other Web Technologies

WebAssembly outperforms JavaScript and asm.js in terms of execution speed. JavaScript is an interpreted language, while asm.js is a subset of JavaScript optimized for performance.

In contrast, WebAssembly executes at near-native speed, thanks to its efficient binary format and ahead-of-time (AOT) compilation. Wasm is language-agnostic, allowing developers to use multiple languages.

JavaScript has a larger developer community and mature tooling, while asm.js requires specific optimizations. WebAssembly binaries are smaller, resulting in faster load times. JavaScript has wider browser compatibility and seamless interoperability with web technologies.

WebAssembly requires explicit interfaces for interaction with JavaScript. Overall, Wasm offers high performance, while JavaScript has wider adoption and tooling support. Usage of asm.js has diminished with the rise of WebAssembly. The choice depends on performance needs, language preferences and browser support.

How Go Helps Create High-Performance Apps

Go is known for its key features that contribute to building high-performance applications. These features include:

Compiled language. Go compiles source code into efficient machine code, which results in fast execution and eliminates the need for interpretation at runtime. The compiled binaries can be directly executed by the operating system, providing excellent performance.

Concurrency support. The language has built-in support for concurrency through goroutines and channels. Goroutines are lightweight threads that allow concurrent execution of functions, while channels facilitate communication and synchronization between goroutines.

This concurrency model makes it easy to write highly concurrent and parallel programs, enabling efficient use of available resources and improving performance in scenarios like handling multiple requests or processing large amounts of data concurrently.

Efficient garbage collection, Go incorporates a garbage collector that automatically manages memory allocation and deallocation. It uses a concurrent garbage collector that minimizes pauses and allows applications to run smoothly without significant interruptions. The garbage collector efficiently reclaims unused memory, preventing memory leaks and enabling efficient memory management in high-performance applications.

Strong standard library. Go comes with a rich standard library that provides a wide range of functionalities, including networking, file I/O, encryption, concurrency primitives and more. The standard library is designed with performance and efficiency in mind, offering optimized implementations and well-designed APIs.

Developers can leverage these libraries to build high-performance applications without relying heavily on third-party dependencies.

Native support for concurrency patterns. Go provides native support for common concurrency patterns, such as mutexes, condition variables and atomic operations. These features enable developers to write thread-safe and efficient concurrent code without the complexities typically associated with low-level synchronization primitives.

This native support simplifies the development of concurrent applications and contributes to improved performance.

Efficient networking. Golang’s standard library includes a powerful networking package that offers efficient abstractions for building networked applications. It provides a robust set of tools for handling TCP/IP, UDP, HTTP, and other protocols. The networking capabilities of Go are designed to be performant, enabling the development of high-throughput and low-latency network applications.

Compilation to standalone binaries. Go can compile code into standalone binaries that contain all the necessary dependencies and libraries. These binaries can be easily deployed and executed on various platforms without requiring the installation of additional dependencies.

This approach simplifies deployment and can contribute to better performance by reducing overhead and ensuring consistent execution environments.

Using Wasm for Computationally Intensive Tasks

Wasm can greatly improve the performance of computationally intensive tasks like image processing or cryptography by leveraging its near-native execution speed. By compiling algorithms or libraries written in languages like C/C++ or Rust to WebAssembly, developers can achieve significant performance gains.

WebAssembly’s efficient binary format and ability to execute in a sandboxed environment make it ideal for running computationally intensive operations in the browser.

Go programs can benefit from improved performance when compiled to Wasm for computationally intensive tasks. For example, Go libraries or applications that involve heavy image manipulation, complex mathematical calculations or cryptographic operations can be compiled to WebAssembly to take advantage of its speed.

Using WebAssembly for UI Rendering

WebAssembly can improve UI rendering performance in the browser compared to traditional JavaScript approaches. By leveraging Wasm’s efficient execution and direct access to low-level operations, rendering engines can achieve faster updates and smoother animations.

WebAssembly allows UI rendering code to run closer to native speeds, resulting in improved user experiences, especially for complex or graphically intensive applications.

UI frameworks or libraries like React or Vue.js can benefit from improved performance when compiled to WebAssembly. By leveraging the speed and efficiency of Wasm, these frameworks can deliver faster rendering and more responsive user interfaces. Compiling UI components written in languages like Rust or C++ to WebAssembly can enhance the overall performance and responsiveness of the UI, making the user experience more seamless and interactive.

Using WebAssembly for Game Development

WebAssembly’s efficient execution and direct access to hardware resources make it ideal for browser-based game development. It offers improved performance compared to traditional JavaScript game engines. By compiling game logic and rendering code to WebAssembly, developers can achieve near-native speeds, enabling complex and visually rich games to run smoothly in the browser.

Go-based game engines like Azul3D can benefit from improved performance when compiled to WebAssembly. By leveraging the speed and efficiency of Wasm, Go game engines can deliver high-performance browser games with advanced graphics and physics simulations.

Compiling Go-based game engines to WebAssembly enables developers to harness Go’s performance characteristics and create immersive gaming experiences that rival native applications.

The Power of Go and WebAssembly: Case Studies

TinyGo

TinyGo is a project that compiles Go code to WebAssembly for running on resource-constrained devices and in the browser. It showcases the performance gains of combining Go with Wasm for scenarios where efficiency and low resource usage are crucial.

Wasmer

Wasmer is an open-source runtime for executing WebAssembly outside the browser. It supports running Go code as WebAssembly modules. Wasmer’s performance benchmarks have demonstrated that Go code executed as Wasm can achieve comparable or better performance than JavaScript in various scenarios.

Vecty

Vecty is a web framework for building responsive and dynamic frontends in Go using WebAssembly. It aims to compete with modern web frameworks like React and VueJS. Here are some key features of Vecty:

  • Simplicity. Vecty is designed to be easily mastered by newcomers, especially those familiar with the Go programming language. It follows Go’s philosophy of simplicity and readability.
  • Performance. Vecty focuses on providing efficient and understandable performance. It aims to generate small bundle sizes, resulting in faster loading times for your web applications. Vecty strives to achieve the same performance as raw JavaScript, HTML  and CSS.
  • Composability. Vecty allows you to nest components, enabling you to build complex user interfaces by logically separating them into smaller, reusable packages. This composability promotes code reusability and maintainability.
  • Designed for Go. Vecty is specifically designed for Go developers. Instead of translating popular libraries from other languages to Go, Vecty was built from the ground up, asking the question, “What is the best way to solve this problem in Go?” This approach ensures that Vecty leverages Go’s unique strengths and idioms.

Best Practices: Developing Web Apps with Wasm and Go

Optimize Go Code for WebAssembly

Minimize memory allocations. Excessive memory allocations can impact performance. Consider using object pooling or reusing memory to reduce the frequency of allocations and deallocations.

Use efficient data structures. Choose data structures that are optimized for performance. Go provides various built-in data structures like slices and maps that are efficient for most use cases.

Limit garbage collection pressure. Excessive garbage collection can introduce pauses and affect performance. Minimize unnecessary object allocations and use the appropriate garbage collection settings to optimize memory management.

Optimize loops and iterations. Identify loops and iterations that can be optimized. Use loop unrolling, minimize unnecessary calculations and ensure efficient memory access patterns.

Leverage goroutines and channels. Go’s concurrency primitives, goroutines, and channels, can help maximize performance. Use them to parallelize tasks and efficiently handle concurrent operations.

Maximize Performance in Wasm Modules

Minimize startup overhead. Reduce the size of the WebAssembly module by eliminating unnecessary code and dependencies. Minify and compress the module to minimize download time.

Optimize data transfers. Minimize data transfers between JavaScript and Wasm modules. Use efficient memory layouts and data representations to reduce serialization and deserialization overhead.

Use SIMD instructions. If applicable, use single instruction, multiple data (SIMD) instructions to perform parallel computations and improve performance. SIMD can be especially beneficial for tasks involving vector operations.

Profile and optimize performance-critical code. Identify performance bottlenecks by profiling the WebAssembly module. Optimize the hot paths, critical functions and sections that consume significant resources to improve overall performance.

Use compiler and optimization flags. Use compiler-specific flags and optimizations tailored for WebAssembly. Different compilers may have specific optimizations to improve performance for Wasm targets.

Minimize Latency and Improve Responsiveness

Reduce round trips. Minimize the number of network requests by combining resources, utilizing caching mechanisms, and employing efficient data transfer protocols like HTTP/2 or WebSockets.

Do asynchronous operations. Use asynchronous programming techniques to avoid blocking the main thread and enhance responsiveness. Employ callbacks, Promises, or async/await syntax for non-blocking I/O operations.

Employ lazy loading and code splitting. Divide the application into smaller modules and load them on-demand as needed. Lazy loading and code splitting reduce the initial load time and improve perceived performance.

Use efficient DOM manipulation. Optimize Document Object Model (DOM) manipulation operations by batching changes and reducing layout recalculations. Use techniques like virtual DOM diffing to minimize updates and optimize rendering.

Rely on caching and prefetching. Leverage browser caching mechanisms and prefetching to proactively load resources that are likely to be needed, reducing latency and improving perceived performance.

Group Created with Sketch.
TNS owner Insight Partners is an investor in: Pragma, fermyon.
THE NEW STACK UPDATE A newsletter digest of the week’s most important stories & analyses.