This Programming Language Boosts Website Performance

January 9, 2019 Posted by Programming 0 thoughts on “This Programming Language Boosts Website Performance”

Introduction

Building large websites that are expected to withstand immense traffic while performing complex calculations requires code that is optimized. Ideally, C++ would be used to ensure optimal performance, but there aren’t enough C++ developers to replace front end developers. Also, C++ can be a tricky language to implement without deep knowledge of its nuances.

A Potential Solution is Rust

Rust is is a low-level language that is strongly typed. Like C/C++, Rust can take full advantage of a machine’s processing power to create systems that are highly performative. It’s also safe. What Rust promises is C++ control without all of the security troublemakers like segfaults, null pointers, buffer overflows, and many other nightmares that C++ developers have to wrangle with.

Rust’s safety, combined with its modularity, makes it suitable for integration with other languages. Thanks to its easy to implement multi threading, we can drastically speed up our single-threaded JavaScript apps.

In the first part of this Rust series, we’re going to walk through setting up Rust, writing Rust code, exporting the code, and using the code in our Node environment. By the end of this article, you’ll see how painless marrying Rust with JavaScript is.

Step 1 — Set up up Node and Rust

In this part of the series we’ll be working with a simple iterative factorial solution written in Rust. Using iteration will allow us to explore mutation and control/flow in Rust. Before we begin, we have to setup our Node and Rust environments.

Node Setup

Here are some tools we’re going to need in step by step order:

1)Nodejs. For those unfamiliar with Node, Node simply allows us to write JavaScript outside of the client. On the command line, you can execute a script with node file.js

  1. Create a project by running: node new project-name.
  2. In the project directory, run: npm init and follow the directions.

It’s time to include some node modules:

2) node-gyp. Be sure to read the node-gyp docs. In it are detailed install instructions for Unix, Mac Os X, and Windows operating systems. You should have Python v. 2.7 installed to avoid errors with node-gyp.

run: npm install node-gyp

3) node-ffi. This library, though flawed, provides a powerful tool-chain that will bind our Rust code to our JavaScript code. During the npm install, it will run node-gyp rebuild so make sure you already have the node-gyp setup secured.

run: npm install node-ffi.

4) ref. The ref module will allow us to read in C types.

We’ll leave our Node project now to set up Rust.

Rust Setup

Install Rust at www.rustup.rs. All you have to do is wait for the packages to load in the command line, just like any package installation.

Next, we’re going to create a library that we’ll make public, so let’s initialize our Rust lib by writing: $ cargo new --lib project_name.

Insert your own project name. For the sake of this tutorial, the name of this project will be called factorial.

Then, go into your Cargo.toml file and add:

[lib]
name=filename
crate-type=[“dylib”]

This setting will overwrite the default rlib and replace it with a dynamic library that our node-ffi library can access.

Step2 — Initialize, Create, and Release a Rust Library

Now that we’ve set up both our Rust and Node projects, let’s stay in our Rust environment and declare our iterative factorialize function in lib.rs. This is for the sake of simplicity. Conventionally, if we were to create an extensive library, we’d wrap this function within a module(mod). We’d also create a file structure that reflects the names of our modules. For more information about library file structure read the crates and modules chapter in the free online Cargo book. Then read, mod and the file-system.

#[no_mangle] 
pub extern fn factorialize(mut num: i64) -> i64 {
if num == 0 || num == 1 {
return 1;
}
let mut cnt = num — 1;
loop {
if cnt < 1 {
break;
}
num = num * cnt;
cnt = cnt -1;
}
return num;
}

Here are some points to note about this code:

  1. #[no_mangle] prevents Rust’s compiler from changing the name of our function.
  2. pub makes our function visible to other programs. By default, all functions are private in Rust.
  3. extern gives our function a foreign function inteface(ffi) that allows access to C libraries.
  4. fn is simply a function declaration.
  5. mut allows us to change the value of our num variable. By default all Rust variables are immutable.
  6. Since Rust is strongly typed, we must specify what type we expect our input to be. In this case, i64 refers to a 64-bit integer.
  7. The type following the arrow -> i64 is the type we expect to return.

After inserting the code, run $ cargo build --release. What we’re concerned about is the .dll file located in target/release/project_name.dll

Note: Your file name may have a .so or .dylib. Both are acceptable variations.

Step3 — Bind Rust to Your Node Environment

We’re ready to bind Rust to our Node environment, so let’s switch over to our Node console. In our index.js file, we’ll require these modules:

const ref = require(‘ref’);
const ffi = require(‘ffi’);
const path = require(‘path’);
const int64 = ref.types.int64; 
const rust=ffi.Library(path.join(‘C:/Sites/factorial/target/release/factor ial.dll’),{
factorialize: [int64, [int64]],
});
rust.factorialize(5); // -> 120

It’s best to have the Rust code at hand in order to avoid errors. If you compare the two snippets of code, the explanation will be clearer.

factorialize(num: i64) -> i64

Here are a couple of takeaways from the JavaScript code:

  1. ref.types.int64: we initialize a variable that takes in a C type that will be read by the ffi module.
  2. ffi.Library() takes two arguments: A path to the dynamic library and an object containing the name of our Rust function as the first key. They key’s value is an array. At index 0, we have the type of the function’s return value. At index 1, we have an array containing the type(s) of the parameter(s).

Note: Make sure your path to the dynamic library you’ve written is correct. In this case, because we created two completely separate projects, using __dirnamewill not be feasible. Though, alternatively you can move the .dll file into your Node project directory to fix the resulting link error.

When we run $ node index.js, we should get 120, after we console.log of course.

Conclusion

We’ve successfully integrated Rust into JavaScript. Still, there are some things to consider before you start replacing all of your JavaScript code with Rust. As for any other optimization technique, you should only integrate Rust for computationally heavy code. If you find that you need to dig into the hardware to squeeze out extra performance, Rust is a good option because of the safety it provides when it comes to memory usage.

Lastly, ffi is very costly when performing arithmetic in any of your functions, and complex data structures further damper performance. The trade-off for ease of use is a performance overhead. Alternatively, you can use Neon or a C++ bridge. Both options are more complex to use, but worth your while if you want to take full advantage of Rust bindings.

 

Please follow and like us:
0
Tags: ,