2023 GopherCon Review

This post is for Day 17 of Merpay Advent Calendar 2023, brought to you by tenling from the Mepay Growth Platform team.

GopherCon is a conference dedicated to the Go programming language, also known as Golang. It’s named after the Go language’s mascot, which is a gopher. The conference typically brings together members of the Go community, including developers, contributors, and enthusiasts, to discuss the language, share knowledge, network, and learn about new tools, libraries, and best practices. This year’s gopherCon was held in a number of countries, and I attended the 9/25-9/28 GopherCon in San Diego.
Let’s take a look back at some of the agenda and activities during that time!

TinyGo

There is a workshop to provide TinyGo boards and example code that you can modify and add your own personal information, like making electronic badge, fill in your name, the company you work for, and your picture (the attached picture is a slack image that I used in the company), I didn’t know we could use golang to compile the program on the Arduino board, it was an eye opener for me!
this is the example code that I got in the workshop: https://github.com/hybridgroup/gophercon-2023
In addition to the GoBadge, there are also circuit boards, soldering tools, and sensors available on site, so we can experience driving various hardware devices with Golang on the spot, which is very interesting.

CTF

CTF activities were also organized during the seminar. The topics were very diverse, ranging from simple cookie issues to difficult anti-translation issues and web vulnerabilities. Some of the topics were related to the session of previous years’ GopherCon, so I watched some of the videos of previous agendas and learned a lot.

"Clean Up Your GOOOP: How to Break OOP Muscle Memory"

There was an impressive session that discussed Golang in relation to object-oriented programming. This is a very exciting session that has provided me with a lot of insights for my development work and a new perspective on OOP, making me understand Go even better.Many people’s first language is an OOP language, when they learn a new language they inevitably get into the habit (or you can say muscle memory) of programming like in the previous language or the first language they learned, and Go as a young programming language is almost never the first language for people, it makes Goop=Go+OOP situation happened that the speaker talked about. The Speaker pointed out some pain points of Gooop, such as:

“Creating separate, Shared components to resolve co-dependency”, which means packages might only contain interfaces and structures without behavior. For example:

// Package shared defines the interfaces and structures used across different services.
package shared

type User struct {
    ID   string
    Name string
}

type UserService interface {
    GetUser(id string) (User, error)
    CreateUser(user User) error
}

This shared package contains definitions of what a User is and what operations can be performed with a User, without dictating how these operations are carried out. To prevent shared components from becoming bloated or developing circular dependencies, ensure that they contain only what’s common and necessary for the interfaces and types they define. Strive for minimalism, providing only the essential shared logic or types needed across different parts of your application.

“Declaring interface as exported provider abstractions”, which means implementation has an Impl suffix, and re-declares every method on the implementation struct. For example:

// Package userimpl provides a concrete implementation of the UserService interface.
package userimpl

import (
    "example/shared"
)

// UserServiceImpl is a concrete implementation of the shared.UserService interface.
type UserServiceImpl struct {
    // Dependencies, like a database connector, go here.
}

func NewUserServiceImpl() *UserServiceImpl {
    return &UserServiceImpl{}
}

func (s *UserServiceImpl) GetUser(id string) (shared.User, error) {
    // Actual implementation goes here...
}

func (s *UserServiceImpl) CreateUser(user shared.User) error {
    // Actual implementation goes here...
}

The userimpl package contains a concrete implementation of the UserService interface from the shared package, adopting the Impl naming convention for the struct that provides this implementation. While the Impl suffix is a common practice, some Go developers prefer to name structs with more descriptive names related to their behavior or underlying technology, like PostgresUserRepository. Doing so can provide more clarity than a generic Impl suffix, especially in larger codebases with multiple implementations of the same interface.

“Architectural Patterns”, which means packages named as pattern layers, and type being repeated in each package, and modify entity/model will need to update multiple packages.

// Package repository for data access layer.
package repository

import (
    "example/shared"
)

// UserRepository defines methods to access user data.
type UserRepository interface {
    FindByID(id string) (*shared.User, error)
    Store(user shared.User) error
}

// Package service for business logic.
package service

import (
    "example/shared"
)

// UserService is the interface that defines business operations available for a User.
type UserService interface {
    GetUser(id string) (shared.User, error)
    CreateUser(user shared.User) error
}

// Package api for the API layer.
package api

import (
    "example/shared"
    "example/service"
)

// UserController handles the HTTP requests related to Users.
type UserController struct {
    userService service.UserService  // Reference to our business logic layer.
}

func (uc *UserController) GetUser(id string) (shared.User, error) {
    // Delegates to the business logic layer.
    return uc.userService.GetUser(id)
}

In this example, each package (repository, service, API) represents a different layer in the architecture. The User type from the shared package is used across these layers, promoting consistency while allowing each layer to focus on its responsibilities. If changes are made to the User model in the shared package, we only need to ensure the interfaces remain satisfied; no redundant code update is needed across multiple layers if the changes don’t affect the service contracts.

As I am writing this blog post, I still feel inspired and hope that GopherCon will release the video recordings of this year’s agenda, so that more people can benefit from hearing this session.

“Balanced GC: A Copying Garbage Collector for Golang”

This session was talking about GC service in ByteDance. GC is Garbage Collect which is a form of automatic memory management that attempts to reclaim garbage, or memory occupied by objects that are no longer in use by the program.

There basically has three types of GC, Serial GC allow only one collector, ParallelGC allow multiple collectors, ConcurrentGC allow mutators and collectors run in the same time, Go simplifies memory management with its advanced garbage collector that employs a concurrent, tri-color mark-sweep algorithm. Integral to the language’s runtime environment, this garbage collector effectively manages memory release, balancing efficiency and performance for high-speed operation.

Go’s garbage collection leverages a sophisticated method that allows for efficient memory cleanup with minimal impact on application performance. This is achieved through a concurrent, tri-color, mark-sweep scheme. Let’s delve into what each component entails.

Concurrent
In the context of Go’s garbage collection, the term "concurrent" indicates that memory cleaning activities are carried out in parallel with the application’s operations. Unlike some traditional garbage collection methods that necessitate a total pause ("stop-the-world"), Go’s GC minimizes interruption. This concurrent operation reduces lengthy pauses and is especially beneficial for systems requiring high availability or real-time responses.

Tri-color
The "tri-color" aspect of Go’s GC refers to a particular stratagem used during memory marking. It divides objects into three categories based on their processing status:
White objects have yet to be evaluated by the garbage collector and their accessibility remains uncertain.
Gray objects have been identified as accessible from the roots but their own references haven’t been fully explored.
Black objects are those that have been fully examined; they and their reachable descendants have been accounted for.
Initially, all objects are labeled as white. The GC begins with root objects, turning them gray and examining them for references to other objects, which then also become gray. Once an object and all its references have been inspected, it turns black. The tri-color approach effectively segregates objects during the mark phase, simplifying the identification of those that are no longer reachable.

Mark-Sweep
The "mark-sweep" descriptor outlines Go’s two-stage process in garbage collection:
The Mark stage involves the GC combing through the memory graph from root objects, marking accessible objects using the tri-color approach detailed above. This stage is performed concurrently, interwoven with the running program, to minimize pauses.
During the Sweep stage, following the marking process, the GC proceeds to reclaim the memory used by white objects deemed unreachable. Consistent with Go’s preference for concurrency, this step is also performed simultaneously with program execution, incrementally freeing up memory that’s no longer in use.
Go’s mark-sweep method is designed to strike a balance, optimizing both program runtime efficiency and memory utilization, which is essential for a wide range of applications reliant on the Go language.

Solution for Balanced GC
Each goroutine is equipped with its dedicated allocation buffer, known as the Goroutine Allocation Buffer (GAB), which encompasses a sizable memory block of 1 KB. This buffer plays a significant role in the efficient and specialized allocation process for certain kinds of memory objects.

Designed to cater to the allocation needs of ‘noscan’ objects—small memory segments that the garbage collector does not need to scan—the GAB efficiently handles objects that are smaller than 128 bytes. Such objects typically do not contain pointers to other objects, which simplifies the memory management process and requires less intervention from the garbage collector.

Managing the GAB involves the coordination of three distinct pointers: base, end, and top. The ‘base’ pointer marks the beginning of the buffer, while the ‘end’ pointer signifies the conclusion of this memory block. The ‘top’ pointer is the dynamic marker that tracks the current position up to where the memory has been allocated.

Memory allocation within the GAB is performed using a technique known as ‘bump pointer allocation.’ This approach is characterized by its simplicity and speed, where the ‘top’ pointer moves, or ‘bumps,’ forward in memory each time a new object is allocated. As long as the ‘top’ pointer has not reached the ‘end’ pointer, indicating that the buffer is full, this allocation method can continue swiftly allocating new objects by merely adjusting the position of the top pointer.

This bump pointer style is particularly efficient because it eliminates the need for complex algorithms to find suitable spots for new objects. Instead, it takes advantage of the contiguous free space provided by the GAB. It also simplifies deallocation, as freeing memory does not require any individual object tracking—once the relevant goroutine is no longer in use, the entirety of its GAB can be reclaimed.

In summary, the GAB is a fine-tuned mechanism that contributes to the language’s performance by optimizing memory allocation for small, straightforward objects, relying on a quick and effective bump pointer system for memory management. Using Balanced GC reduced peak CPU usage by 4.6% and decreased the latency of core interfaces by 4.5% to 7.7%.

Attending GopherCon for the first time was an enlightening experience that expanded my perspective as a developer. I gained numerous insights and hope that you, too, can benefit from my recap of the event.

Other participants shared their experiences and session summaries from GopherCon in mercari.go #24. The Mercari group regularly organizes meetups related to Go, and if you’re interested, please follow us on Connpass and Meetup to learn more.
meetup: https://www.meetup.com/mercaridev/
connpass: https://mercari.connpass.com/

Tomorrow’s article will be by Masamichi San. Look forward to it!

  • X
  • Facebook
  • linkedin
  • このエントリーをはてなブックマークに追加