Why is my rust slower than my C - memory manipulation? - c

I have two implmentations that yield the same Vec in the end, however my C implementation is about 2x as fast as the Rust implementation. Here are my implementations:
extern {
fn array_test(buffer: *mut u8, length: u32);
}
fn main() {
let mut vectest = Vec::<u8>::with_capacity(2 << 20);
unsafe { // using C function
vectest.set_len(2 << 20);
let start = SystemTime::now();
array_test(vectest.as_mut_ptr(), 2 << 20);
let end = SystemTime::now();
println!("C func took {:?}", end.duration_since(start));
};
let mut vectest2 = Vec::<u8>::with_capacity(2 << 20);
unsafe { // pure Rust
vectest2.set_len(2 << 20);
let start = SystemTime::now();
let vectest2_ptr = vectest2.as_mut_ptr();
for i in (0..2 << 20).step_by(4) {
*(vectest2_ptr.add(i) as *mut u32) = i as u32;
}
let end = SystemTime::now();
println!("Rust took {:?}", end.duration_since(start));
};
}
C function:
void array_test(char *buffer, unsigned int len) {
for (unsigned int i = 0; i < len; i += 4) {
(*(int *)(buffer+i)) = i;
}
}
One instance of time results from running cargo run --release is:
C func took Ok(440.692ยตs)
Rust took Ok(921.658ยตs)
About every time I run it the C finishes twice as fast.
Is this just due to poor Rust code code optimization on my part? If so, what could I do to close the gap between my C and my Rust?

If you re-write your loop:
for i in (0..2 << 20).step_by(4) {
*(vectest2_ptr.add(i) as *mut u32) = i as u32;
}
as
let mut i = 0;
while i <= (2 << 20) {
*(vectest2_ptr.add(i) as *mut u32) = i as u32;
i += 4;
}
the performance difference you're seeing goes away.
From a quick glance at the assembly on rust.godbolt.org (using -O as argument for rustc) it looks like rustc/llvm is able to vectorize (with SIMD) the loop when using the while variant, but it isn't applying that same optimization to your for loop.

Related

How to build a str in Rust [duplicate]

This question already has answers here:
How to handle "borrowed value does not live long enough" error when finding the longest substring of consecutive equal characters?
(2 answers)
Closed last month.
Suppose I have a char in the variable c and a positive int in the variable n. I want to build the str containing c occurring n times. How can I do it?
I tried building it as a String, and maybe I just got dizzy trying to read the documentation on strings, but I couldn't see how to convert it to a str. But then if I'm trying to just build it as a str directly then I couldn't see how to do that either.
For context, here is the full function I'm trying to implement. It takes a string and finds the longest sequence of consecutive characters (and breaks ties by taking the first that occurs).
pub fn longest_sequence(s: &str) -> Option<&str> {
if s.len() == 0 { return None; }
let mut current_c = s.as_bytes()[0] as char;
let mut greatest_c = s.as_bytes()[0] as char;
let mut current_num = 0;
let mut greatest_num = 0;
for ch in s.chars() {
if current_c == ch {
current_num += 1;
if current_num > greatest_num {
greatest_num = current_num;
greatest_c = current_c;
}
} else {
current_num = 1;
current_c = ch;
}
}
// Now build the output str ...
}
I think there are a couple of misconceptions about str vs String.
str can never exist alone. It is always used as &str (or Box<str> or *str, but in your case those shouldn't matter).
&str does not own any data. It is merely a reference to (parts of) another String.
String actually holds data.
So when you want to return data, use String; if you want to reference existing data, return &str.
There is no way to convert a local String to a &str. Somewhere the data has to be stored, and &str doesn't store it. (for completeness sake: Yes you could leak it, but that would create a permanent string in memory that will never go away again)
So in your case there are two ways:
Reference the input &str, because somewhere its data is already stored.
Return a String instead.
As a side note: do not do s.as_bytes()[0] as char, as it will not work with UTF8-strings. Rust strings are defined as UTF8.
Here is one possible solution:
pub fn longest_sequence(s: &str) -> Option<&str> {
let mut current_c = s.chars().next()?;
let mut current_start = 0;
let mut current_len = 0;
let mut greatest: &str = "";
let mut greatest_len = 0;
for (pos, ch) in s.char_indices() {
if current_c == ch {
current_len += 1;
} else {
if greatest_len < current_len {
greatest = &s[current_start..pos];
greatest_len = current_len;
}
current_len = 1;
current_c = ch;
current_start = pos;
}
}
if greatest_len < current_len {
greatest = &s[current_start..];
}
Some(greatest)
}
pub fn main() {
let s = "๐Ÿคช๐Ÿ˜๐Ÿ˜๐Ÿ˜๐Ÿ˜‰โ‚ฌโ‚ฌ๐Ÿคช๐Ÿคช";
let seq = longest_sequence(s);
println!("{:?}", seq);
}
Some("๐Ÿ˜๐Ÿ˜๐Ÿ˜")
Some explanations:
No need to check for empty string. s.chars().next()? does so automatically.
Use s.chars().next() instead of s.as_bytes()[0] as char, as the second one is not UTF8 compatible.
I explicitely store greatest_len instead of using greatest.len() because greatest.len() is also not UTF8 compatible as it gives you the size of the string in bytes, not in chars.
You stored the new largest string whenever a new char of the same value was found; I had to move it to the case where the char type changed (and once after the loop), because we don't yet know the end of the current char. Again, note that &s[current_start..current_start+current_len] wouldn't work, because &s[ .. ] wants indices in bytes, but current_len is in chars. So we need to wait for another char to know where the previous one ended.
Another solution, based on your code, would be:
pub fn longest_sequence(s: &str) -> Option<String> {
let mut current_c = s.chars().next()?;
let mut greatest_c = current_c;
let mut current_num = 0;
let mut greatest_num = 0;
for ch in s.chars() {
if current_c == ch {
current_num += 1;
if current_num > greatest_num {
greatest_num = current_num;
greatest_c = current_c;
}
} else {
current_num = 1;
current_c = ch;
}
}
// Build the output String
Some(std::iter::repeat(greatest_c).take(greatest_num).collect())
}
pub fn main() {
let s = "๐Ÿคช๐Ÿ˜๐Ÿ˜๐Ÿ˜๐Ÿ˜‰โ‚ฌโ‚ฌ๐Ÿคช๐Ÿคช";
let seq = longest_sequence(s);
println!("{:?}", seq);
}
Some("๐Ÿ˜๐Ÿ˜๐Ÿ˜")
To convert a String to &'static str you need to leak it like this:
fn leak(s: String) -> &'static str {
let ptr = s.as_str() as *const str;
core::mem::forget(s);
unsafe {&*ptr}
}
And char to String:
fn cts(c: char, n: usize) -> String {
(0..n)
.map(|_| c)
.collect()
}
So char to &'static str basically will look like this:
fn conv(c: char, n: usize) -> &'static str {
leak(cts(c, n))
}
I do not recommend to leak the String tho, just use it as is.

Why does this array not get fouled up?

I'm studying code to learn about Extended USB Controls and I came across this bit of code shown below. The function reverses an array's order. It's pretty straight forward, except for one thing. Why doesn't the code corrupt the array? Using the source and destination as the same variable should corrupt it, shouldn't it?
/*
* Convert a array of bytes from big endian to little endian and vice versa by inverting it
*/
static
uint8_t *raw_inv(uint8_t *data, int size) {
int ai = 0;
int bi = size - 1;
uint8_t a = 0;
uint8_t b = 0;
while (ai < bi) {
a = data[ai];
b = data[bi];
data[ai] = b;
data[bi] = a;
ai++;
bi--;
}
return data;
}
Ah: It's the 'static' declaration, isn't it?
It uses a and b as temporaries to hold the values it's exchanging. Only one temporary is needed -- this could be rewritten as:
while (ai < bi) {
a = data[ai];
data[ai] = data[bi];
data[bi] = a;
ai++;
bi--;
}

figure out why my RC4 Implementation doesent produce the correct result

Ok I am new to C, I have programmed in C# for around 10 years now so still getting used to the whole language, Ive been doing great in learning but im still having a few hickups, currently im trying to write a implementation of RC4 used on the Xbox 360 to encrypt KeyVault/Account data.
However Ive run into a snag, the code works but it is outputting the incorrect data, I have provided the original c# code I am working with that I know works and I have provided the snippet of code from my C project, any help / pointers will be much appreciated :)
Original C# Code :
public struct RC4Session
{
public byte[] Key;
public int SBoxLen;
public byte[] SBox;
public int I;
public int J;
}
public static RC4Session RC4CreateSession(byte[] key)
{
RC4Session session = new RC4Session
{
Key = key,
I = 0,
J = 0,
SBoxLen = 0x100,
SBox = new byte[0x100]
};
for (int i = 0; i < session.SBoxLen; i++)
{
session.SBox[i] = (byte)i;
}
int index = 0;
for (int j = 0; j < session.SBoxLen; j++)
{
index = ((index + session.SBox[j]) + key[j % key.Length]) % session.SBoxLen;
byte num4 = session.SBox[index];
session.SBox[index] = session.SBox[j];
session.SBox[j] = num4;
}
return session;
}
public static void RC4Encrypt(ref RC4Session session, byte[] data, int index, int count)
{
int num = index;
do
{
session.I = (session.I + 1) % 0x100;
session.J = (session.J + session.SBox[session.I]) % 0x100;
byte num2 = session.SBox[session.I];
session.SBox[session.I] = session.SBox[session.J];
session.SBox[session.J] = num2;
byte num3 = data[num];
byte num4 = session.SBox[(session.SBox[session.I] + session.SBox[session.J]) % 0x100];
data[num] = (byte)(num3 ^ num4);
num++;
}
while (num != (index + count));
}
Now Here is my own c version :
typedef struct rc4_state {
int s_box_len;
uint8_t* sbox;
int i;
int j;
} rc4_state_t;
unsigned char* HMAC_SHA1(const char* cpukey, const unsigned char* hmac_key) {
unsigned char* digest = malloc(20);
digest = HMAC(EVP_sha1(), cpukey, 16, hmac_key, 16, NULL, NULL);
return digest;
}
void rc4_init(rc4_state_t* state, const uint8_t *key, int keylen)
{
state->i = 0;
state->j = 0;
state->s_box_len = 0x100;
state->sbox = malloc(0x100);
// Init sbox.
int i = 0, index = 0, j = 0;
uint8_t buf;
while(i < state->s_box_len) {
state->sbox[i] = (uint8_t)i;
i++;
}
while(j < state->s_box_len) {
index = ((index + state->sbox[j]) + key[j % keylen]) % state->s_box_len;
buf = state->sbox[index];
state->sbox[index] = (uint8_t)state->sbox[j];
state->sbox[j] = (uint8_t)buf;
j++;
}
}
void rc4_crypt(rc4_state_t* state, const uint8_t *inbuf, uint8_t **outbuf, int buflen)
{
int idx = 0;
uint8_t num, num2, num3;
*outbuf = malloc(buflen);
if (*outbuf) { // do not forget to test for failed allocation
while(idx != buflen) {
state->i = (int)(state->i + 1) % 0x100;
state->j = (int)(state->j + state->sbox[state->i]) % 0x100;
num = (uint8_t)state->sbox[state->i];
state->sbox[state->i] = (uint8_t)state->sbox[state->j];
state->sbox[state->j] = (uint8_t)num;
num2 = (uint8_t)inbuf[idx];
num3 = (uint8_t)state->sbox[(state->sbox[state->i] + (uint8_t)state->sbox[state->j]) % 0x100];
(*outbuf)[idx] = (uint8_t)(num2 ^ num3);
printf("%02X", (*outbuf)[idx]);
idx++;
}
}
printf("\n");
}
Usage (c#) :
byte[] cpukey = new byte[16]
{
...
};
byte[] hmac_key = new byte[16]
{
...
};
byte[] buf = new System.Security.Cryptography.HMACSHA1(cpukey).ComputeHash(hmac_key);
MessageBox.Show(BitConverter.ToString(buf).Replace("-", ""), "");
Usage(c):
const char cpu_key[16] = { 0xXX, 0xXX, 0xXX };
const unsigned char hmac_key[16] = { ... };
unsigned char* buf = HMAC_SHA1(cpu_key, hmac_key);
uint8_t buf2[20];
uint8_t buf3[8] = { 0x1E, 0xF7, 0x94, 0x48, 0x22, 0x26, 0x89, 0x8E }; // Encrypted Xbox 360 data
uint8_t* buf4;
// Allocated 8 bytes out.
buf4 = malloc(8);
int num = 0;
while(num < 20) {
buf2[num] = (uint8_t)buf[num]; // convert const char
num++;
}
rc4_state_t* rc4 = malloc(sizeof(rc4_state_t));
rc4_init(rc4, buf2, 20);
rc4_crypt(rc4, buf3, &buf4, 8);
Now I have the HMACsha1 figured out, im using openssl for that and I confirm I am getting the correct hmac/decryption key its just the rc4 isnt working, Im trying to decrypt part of the Kyevault that should == "Xbox 360"||"58626F7820333630"
The output is currently : "0000008108020000" I do not get any errors in the compilation, again any help would be great ^.^
Thanks to John's help I was able to fix it, it was a error in the c# version, thanks John !
As I remarked in comments, your main problem appeared to involve how the output buffer is managed. You have since revised the question to fix that, but I describe it anyway here, along with some other alternatives for fixing it. The remaining problem is discussed at the end.
Function rc4_crypt() allocates an output buffer for itself, but it has no mechanism to communicate a pointer to the allocated space back to its caller. Your revised usage furthermore exhibits some inconsistency with rc4_crypt() with respect to how the output buffer is expected to be managed.
There are three main ways to approach the problem.
Function rc4_crypt() presently returns nothing, so you could let it continue to allocate the buffer itself, and modify it to return a pointer to the allocated output buffer.
You could modify the type of the outbuf parameter to uint8_t ** to enable rc4_crypt() to set the caller's pointer value indirectly.
You could rely on the caller to manage the output buffer, and make rc4_crypt() just write the output via the pointer passed to it.
The only one of those that might be tricky for you is #2; it would look something like this:
void rc4_crypt(rc4_state_t* state, const uint8_t *inbuf, uint8_t **outbuf, int buflen) {
*outbuf = malloc(buflen);
if (*outbuf) { // do not forget to test for failed allocation
// ...
(*outbuf)[idx] = (uint8_t)(num2 ^ num3);
// ...
}
}
And you would use it like this:
rc4_crypt(rc4, buf3, &buf4, 8);
... without otherwise allocating any memory for buf4.
The caller in any case has the responsibility for freeing the output buffer when it is no longer needed. This is clearer when it performs the allocation itself; you should document that requirement if rc4_crypt() is going to be responsible for the allocation.
The remaining problem appears to be strictly an output problem. You are apparently relying on print statements in rc4_crypt() to report on the encrypted data. I have no problem whatever with debugging via print statements, but you do need to be careful to print the data you actually want to examine. In this case you do not. You update the joint buffer index idx at the end of the encryption loop before printing a byte from the output buffer. As a result, at each iteration you print not the encrypted byte value you've just computed, but rather an indeterminate value that happens to be in the next position of the output buffer.
Move the idx++ to the very end of the loop to fix this problem, or change it from a while loop to a for loop and increment idx in the third term of the loop control statement. In fact, I strongly recommend for loops over while loops where the former are a good fit to the structure of the code (as here); I daresay you would not have made this mistake if your loop had been structured that way.

How to modify the last item of an array?

Since arr is borrowed as mutable, the length of arr can't be gotten by calling len(). I'm stuck here, what's the right way to do it?
fn double_last(arr: &mut[i32]) -> &i32 {
let last = &mut arr[arr.len() - 1]; // borrow checker error.
//let last = &mut arr[3]; // fine
*last *= 2;
last
}
fn main() {
let mut a = [1,2,3,4];
println!("{}", double_last(&mut a));
println!("{:?}", a);
}
If you only need the last, you can use std::slice::last_mut
fn double_last(arr: &mut[i32]) -> &i32 {
let last = arr.last_mut().unwrap();
*last *= 2;
last
}
This will hopefully be fixed with the introduction of non-lexical lifetimes and the accompanying changes soon into the future (seems like it could be solved?).
For now though, you can satisfy the borrow checker by splitting that calculation out:
let n = arr.len() - 1;
let last = &mut arr[n];

How to read a matrix from a txt file in Rust?

File example; square matrix; size of matrix after #
#3
1.1 -0.2 0.1
0.1 -1.2 -0.2
0.2 -0.1 1.1
Approximately so i would write it in C
double **A;
int i,j,size=0;
FILE *f=NULL;
f=fopen("input.txt","w");
fscanf(f,"#%d\n",&size);
A=(double**)malloc(size*sizeof(double*));
for(i=0;i<size;i++)
A[i]=(double*)malloc(size*sizeof(double));
for(i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
fscanf(f,"%lf",&A[i][j]);
}
}
fclose(f);
I tried to use the method "read_to_string" and parse String, but I'm confused by the conversion between String and str.
This is a naive translation of your code to Rust:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
// open the file
let mut f = BufReader::new(File::open("input.txt").unwrap());
// read the first line and extract the number from it
let mut num_line = String::new();
f.read_line(&mut num_line).unwrap();
let n: usize = num_line[1..].trim().parse().unwrap();
// preallocate the array and read the data into it
let mut arr = vec![vec![0f64; n]; n];
for (i, line) in f.lines().enumerate() {
for (j, number) in line.unwrap().split(char::is_whitespace).enumerate() {
arr[i][j] = number.trim().parse().unwrap();
}
}
println!("{:?}", arr);
}
There is more idiomatic way to perform the loop in Rust, though:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
let mut f = BufReader::new(File::open("input.txt").unwrap());
let mut num_line = String::new();
f.read_line(&mut num_line).unwrap();
let n: usize = num_line[1..].trim().parse().unwrap();
let arr: Vec<Vec<f64>> = f.lines()
.take(n)
.map(|l| l.unwrap().split(char::is_whitespace)
.take(n)
.map(|number| number.parse().unwrap())
.collect())
.collect();
println!("{:?}", arr);
}
In fact, you don't even need the number of lines in advance to read the data if the format of your file is completely fixed:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
let mut f = BufReader::new(File::open("input.txt").unwrap());
let mut s = String::new();
f.read_line(&mut s).unwrap();
let arr: Vec<Vec<f64>> = f.lines()
.map(|l| l.unwrap().split(char::is_whitespace)
.map(|number| number.parse().unwrap())
.collect())
.collect();
println!("{:?}", arr);
}

Resources