DZone
Performance Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Performance Zone > Why We Switched From Python to Go, Part 2

Why We Switched From Python to Go, Part 2

We look at performance issues surrounding Python and Go, why Go looks to be the better language for writing performant code, and throw in a look at Elixir just for fun.

Thierry Schellenbach user avatar by
Thierry Schellenbach
·
Oct. 30, 17 · Performance Zone · Opinion
Like (4)
Save
Tweet
9.54K Views

Join the DZone community and get the full member experience.

Join For Free

Disadvantages of Using Golang

Disadvantage 1 - Lack of Frameworks

Go doesn't have a single dominant framework like Rails for Ruby, Django for Python or Laravel for PHP. This is a topic of heated debate within the Go community, as many people advocate that you shouldn't use a framework to begin with. I totally agree that this is true for some use cases. However, if someone wants to build a simple CRUD API they will have a much easier time with Django/DJRF, Rails Laravel or Phoenix.

Update: as the comments pointed out there are several projects that provide a framework for Go. Revel, Iris, Echo, Macaron, and Buffalo seem to be the leading contenders. For Stream's use case, we prefer to not use a framework. However for many new projects that are looking to provide a simple CRUD API the lack of a dominant framework will be a serious disadvantage.

Disadvantage 2 - Error Handling

Go handles errors by simply returning an error from a function and expecting your calling code to handle the error (or to return it up the calling stack). While this approach works, it's easy to lose scope of what went wrong to ensure you can provide a meaningful error to your users. The errors package solves this problem by allowing you to add context and a stack trace to your errors.

Another issue is that it's easy to forget to handle an error by accident. Static analysis tools like errcheck and megacheck are handy to avoid making these mistakes.

While these workarounds work well it doesn't feel quite right. You'd expect proper error handling to be supported by the language.

Disadvantage 3 - Package Management

Go's package management is by no means perfect. By default, it doesn't have a way to specify a specific version of a dependency and there's no way to create reproducible builds. Python, Node, and Ruby all have better systems for package management. However, with the right tools, Go's package management works quite well.

You can use Dep to manage your dependencies to allow specifying and pinning versions. Apart from that, we've contributed an open-source tool called VirtualGo which makes it easier to work on multiple projects written in Go.

Python vs Go

One interesting experiment we conducted was taking our ranked feed functionality in Python and rewriting it in Go. Have a look at this example of a ranking method:

{
"functions": {
"simple_gauss": {
"base": "decay_gauss",
"scale": "5d",
"offset": "1d",
"decay": "0.3"
},
"popularity_gauss": {
"base": "decay_gauss",
"scale": "100",
"offset": "5",
"decay": "0.5"
}
},
"defaults": {
"popularity": 1
},
"score": "simple_gauss(time)*popularity"
}

Both the Python and Go code need to do the following to support this ranking method:

  1. Parse the expression for the score. In this case, we want to turn this string "simple_gauss(time)*popularity" into a function that takes an activity as input and returns a score as output.
  2. Create partial functions based on the JSON config. For example, we want "simple_gauss" to call "decay_gauss" with a scale of 5 days, offset of 1 day, and a decay factor of 0.3.
  3. Parse the "defaults" configuration so you have a fallback if a certain field is not defined on an activity.
  4. Use the function from step 1 to score all activities in the feed.

Developing the Python version of the ranking code took roughly 3 days. That includes writing the code, unit tests, and documentation. Next, we've spent approximately 2 weeks optimizing the code. One of the optimizations was translating the score expression (simple_gauss(time)*popularity) into an abstract syntax tree. We also implemented caching logic which pre-computed the score for certain times in the future.

In contrast, developing the Go version of this code took roughly 4 days. The performance didn't require any further optimization. So while the initial bit of development was faster in Python, the Go-based version ultimately required substantially less work from our team. As an added benefit, the Go code performed roughly 40 times faster than our highly-optimized Python code.

Now, this is just a single example of the performance gains we've experienced by switching to Go. It is, of course, comparing apples to oranges:

  • The ranking code was my first project in Go.
  • The Go code was built after the Python code, so the use case was better understood.
  • The Go library for expression parsing was of exceptional quality.

Your mileage will vary. Some other components of our system took substantially more time to build in Go compared to Python. As a general trend, we see that developing Go code takes slightly more effort. However, we spend much less time optimizing the code for performance.

Elixir vs Go - The Runner Up

Another language we evaluated is Elixir. Elixir is built on top of the Erlang virtual machine. It's a fascinating language and we considered it since one of our team members has a ton of experience with Erlang.

For our use cases, we noticed that Go's raw performance is much better. Both Go and Elixir will do a great job serving thousands of concurrent requests. However, if you look at individual request performance, Go is substantially faster for our use case. Another reason why we chose Go over Elixir was the ecosystem. For the components we required, Go had more mature libraries whereas, in many cases, the Elixir libraries weren't ready for production usage. It's also harder to train/find developers to work with Elixir.

These reasons tipped the balance in favor of Go. The Phoenix framework for Elixir looks awesome though and is definitely worth a look.

Conclusion

Go is a very performant language with great support for concurrency. It is almost as fast as languages like C++ and Java. While it does take a bit more time to build things using Go compared to Python or Ruby, you'll save a ton of time spent on optimizing the code.

Go's combination of a great ecosystem, easy onboarding for new developers, fast performance, solid support for concurrency, and a productive programming environment makes it a great choice.

We won't be saying goodbye to Python anytime soon, but going forward all performance-intensive code will be written in Go.

Python (language) code style Elixir (programming language)

Published at DZone with permission of Thierry Schellenbach, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Choosing Between GraphQL Vs REST
  • Growth in Java Development for Web and Mobile Apps
  • The Right Way to Hybridize Your Product Development Technique
  • The Evolution of Configuration Management: IaC vs. GitOps

Comments

Performance Partner Resources

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo