Sale!

ECE 522 Lab 3: Benchmarking and Databases solution

$30.00 $25.50

Category:

Description

5/5 - (3 votes)

Software Construction, Verification and Evolution
Part 1: Benchmarking in Rust
Consider the following x86 machine code:
Details of x86 machine code can be found at:
http://flint.cs.yale.edu/cs421/papers/x86-asm/asm.html
and also, at:

.LCPI0_0:
.long 10
.long 0
.long 12
.long 32
example::main:
sub rsp, 16
movaps xmm0, xmmword ptr [rip + .LCPI0_0]
movups xmmword ptr [rsp], xmm0
xor eax, eax
cmp dword ptr [rsp + 4], 32
setg al
mov dword ptr [rsp + 8*rax + 4], 10
add rsp, 16
ret
Question 1: Examine the machine code and explain what it does? (Hint: What can you do to a list with
two for loops and a single conditional?)
Question 2: Translate the machine code to Rust.
Question 3: Use the compiler explorer (https://godbolt.org/) to generate the machine code for your
answer in Question 2.
Question 4: Does the optimization flag -O make a difference?
● DEMO this deliverable to the lab instructor.
Criterion (https://github.com/bheisler/criterion.rs) is a statistics-driven micro benchmarking crate in
Rust. It helps you write fast code by detecting and measuring performance improvements or regressions,
even small ones, quickly and accurately. The crate helps us optimize with confidence, knowing how each
change affects the performance of our code.
1
ECE 522 | Software Construction, Verification and Evolution
Question 5: Follow the instructions in the quickstart section
(https://github.com/bheisler/criterion.rs#quickstart ) to add the Criterion crate to your project (Question
2).
Question 6: Bench your function from Question 2 using the following code:
let mut rng = rand::thread_rng();
let mut l: Vec = (0..10000).map(|_| {rng.gen_range(1, 10000)}).collect();
c.bench_function(“your function: “, |b| b.iter(|| your_function::(black_box(&mut
l))));
Feel free to replace “your_code” with the name of the function you wrote in Question 2 and examine the
output of the benchmark.
● DEMO this deliverable to the lab instructor.
Part 2: Code Optimization
Based on the results obtained in Question 6, Do you see any room for optimization? (Hint: consider using
iterators (https://doc.rust-lang.org/std/iter/trait.Iterator.html)
Question 7: Optimize your code from Question 2.
Question 8: Bench your function from Question 7 using the following code:
let mut rng = rand::thread_rng();
let mut l: Vec = (0..10000).map(|_| {rng.gen_range(1, 10000)}).collect();
c.bench_function(“your function: “, |b| b.iter(|| your_function::(black_box(&mut
l))));
Question 9: Compare between the two performance results obtained from Criterion crate from Question
6 and Question 8.
Question 10: What are “zero-cost abstractions” in Rust? Explain how this term applies to Question 9.
● DEMO (and Explain) this deliverable to the lab instructor.
Part 3: Managing Databases
Modern applications are rapidly evolving to become data-heavy systems. For example, Training machines
with data is starting to become a mainstream way to solve complex problems. As a result, the data
should be easy to retrieve and must have guarantees regarding consistency and durability. Databases are
the go-to solution for providing a robust foundation for building applications that are data-backed and
that support the expectations of their users. A database is a collection of tables. Tables are units of data
organization. Data that’s been organized into tables is only applicable to relational databases. Other
databases, such as NoSQL and graph-based databases, use a more flexible document model to store and
2
ECE 522 | Software Construction, Verification and Evolution
organize data. Tables are usually representations of entities from the real world. These entities can have
various attributes that take the place of columns in these tables.
In this part, we will use SQLite to design a convenient database system to provide data storage for a
simple Bank application written in Rust.
1- SQLite Integration
First, we need need to install SQLite in order to be able to create our database.
● For windows:
1- Install SQLite for their website (https://www.sqlite.org/download.html).
2- Download the sqlite-shell-win32-*.zip and sqlite-dll-win32-*.zip zipped files.
3- Create a folder on your machine (e.g., C:\sqlite) and unzip these two zipped files in this folder.
4- Add the path for this folder (e.g., C:\sqlite) to your PATH envrionment variable.
5- To test the installation, go to the terminal and write sqlite3 comand, the following message
should be displayed.
● For Linux:
SQLite should be installed by default on almost all the flavours of Linux OS. You can test the
installation by issuing the sqlite3 command in the terminal.
● For Mac OS:
1- Although the lates version of Mac should have SQLite installted by default, you can insall it
from their website (https://www.sqlite.org/download.html).
2- Download sqlite-autoconf-*.tar.gz from source code section.
3- Run the following command −
$tar xvfz sqlite-autoconf-3071502.tar.gz
$cd sqlite-autoconf-3071502
$./configure –prefix=/usr/local
$make
$make install
4- To test the intallation, run the following command on your machnie:
$sqlite3
3
ECE 522 | Software Construction, Verification and Evolution
Second, once you have SQLite installed, you can create a new Rust project, then create a new folder
called data. Your project’s folders should have the following folders/files:
● Cargo has generated a new directory hello-rust with the following files:
DBProject
├── Cargo.toml
└── data
└── src
└── main.rs
1 directory, 2 files
Navigate to the data folder and run the following command to create your database
sqlite3 users.db
Your database will have two tables:
1- One for username and password
2- Another one to store a list of transactions.
● To create the user table that stores usernames and passwords for bank clients, we can run the
following command:
sqlite> create table users(u_name text PRIMARY KEY, p_word text);
To display a list of tables in your database, you can use:
sqlite> .tables
This should return:
users
● the transactions table should contain the following fields:
• u_from: the user from which the transaction is sent.
• u_to: the user to which the transaction is going.
• t_date: the transaction date
• t_amount: the transaction amount
• The primary key is using multiple names combining u_from and t_date.
● To create the table:
4
ECE 522 | Software Construction, Verification and Evolution
sqlite> create table transactions(u_from text, u_to text, t_date integer, t_amount
text, PRIMARY KEY(u_from,t_date), FOREIGN KEY (u_from) REFERENCES users(u_name),
FOREIGN KEY (u_to) REFERENCES users(u_name));
• The table has two foreign keys that reference the u_name from the users table.
Now running the .tables command:
sqlite> .tables
should return:
transactions users
Third, to insert data into the users database, we can use the insert statemetn as follows:
sqlite> insert into users (u_name, p_word) values (“Matt”, “matt_pw”), (“Dave”,
“dave_pw”);
sqlite> insert into transactions (u_from, u_to, t_date, t_amount) values
(“Dave”,”Matt”,datetime(“now”),50);
We can use the phrase datetime(“now”), which is a SQL function to return the current time.
To view the data, we can use the select statement:
sqlite> select * from users;
Matt|matt_pw
Dave|dave_pw
sqlite> select * from transactions;
Dave|Matt|2019-10-15 01:54:09|50
To delete from your tables, you can use:
sqlite> delete from transactions;
sqlite> delete from users;
2- Data Management using Rust
First, we should not save passwords as text. To make secure passwords, we will use the bcrypt library:
From that bcrypt, we will need the methods: DEFAULT_COST, hash, verify; and the error type BcryptError.
5
ECE 522 | Software Construction, Verification and Evolution
Also, to manage our SQLite database from our code, we need SQLite crate:
Second, we will need to create a struct UserBase to handle access to the database. So, we will add the
following to our main.rs file
pub struct UserBase{
fname:String,
}
We are going to combine two different error types: bcrypt errors and the SQLite errors. We can use
another OR structure by implementing the erros as enum:
use bcrypt::{DEFAULT_COST, hash, verify, BcryptError};
use sqlite::Error as SqErr;
#[derive(Debug )]
pub enum UBaseErr{
DbErr(SqErr),
HashError(BcryptError)
}
To handle both types of error as UBaseErr, we can use the From trait allows for a type to define how to
create itself from another type, hence providing a very simple mechanism for converting between
several types.
impl From for UBaseErr{
fn from(s:SqErr)->Self{
UBaseErr::DbErr(s)
}
}
impl From for UBaseErr{
fn from(b:BcryptError)->Self{
UBaseErr::HashError(b)
}
}
6
ECE 522 | Software Construction, Verification and Evolution
Third, let’s implement some functions for the UserBase struct. We will start with the add_user method
for adding a user to the database:
impl UserBase{
pub fn add_user(&self, u_name:&str, p_word:&str)->Result<(),UBaseErr>{
let conn=sqlite::open(&self.fname)?;
let hpass=bcrypt::hash(p_word,DEFAULT_COST)?;
let mut st= conn.prepare(“insert into users(u_name, p_word) values (?,?);”)?;
st.bind(1,u_name)?;
st.bind(2,&hpass as &str)?;
st.next()?;
Ok(())
}
}
Now, can you write the pay function that inserts into the transaction table:
pub fn pay(&self, u_from:&str, u_to:&str, amount:i64)->Result<(),UBaseErr>{
let conn=sqlite::open(&self.fname)?;
let mut st= conn.prepare(“insert into transactions (u_from, u_to, t_date,
t_amount) values(?,?,datetime(\”now\”),?);”)?;
st.bind(1,u_from)?;
st.bind(2,u_to)?;
st.bind(3,amount)?;
st.next()?;
Ok(())
}
3- Writing Tests
Question 11: Write test cases to check if the users table gets updated when the add_user function is run
and the transactions table gets updated when the pay function is run. Your tests should ensure that your
code runs properly.
● DEMO this deliverable to the lab instructor.
7