Rust Slow to Normal Python 3.12

i can check the speed test rust and python 3.12
but result is not good i think so rust create to increase python 3.12 code execution speed
please can you explain

Rust Code

use std::time::Instant;
use std::collections::HashMap;

pub fn test_list_operations() {
    // Initialize a vector with numbers from 0 to 99999
    let mut vec: Vec<usize> = (0..100_000).collect();
    let start_time = Instant::now();
    
    // Perform a series of operations 1000 times
    for _ in 0..1000 {
        // Append the current length of the vector to the end
        vec.push(vec.len());
        // Remove the first element of the vector
        vec.remove(0);
    }
    
    // Calculate the total elapsed time
    let end_time = start_time.elapsed();
    println!("List operations took {:?}", end_time);
}

pub fn test_dict_operations() {
    // Initialize a HashMap with keys from 0 to 9999 and values as their squares
    let mut dict: HashMap<usize, usize> = (0..10_000).map(|i| (i, i * i)).collect();
    // Add the key 0 with a value of 0 to the dictionary
    dict.insert(0, 0);
    let start_time = Instant::now();
    
    // Perform a series of operations 1000 times
    for _ in 0..1000 {
        // Insert a new key-value pair where the key is the current size of the dictionary
        dict.insert(dict.len(), dict.len() * dict.len());
        // Check if the key 0 exists in the dictionary
        if dict.contains_key(&0) {
            // Remove the key 0 from the dictionary
            dict.remove(&0);
        }
    }
    
    // Calculate the total elapsed time
    let end_time = start_time.elapsed();
    println!("Dictionary operations took {:?}", end_time);
}

pub fn test_loop() {
    let start_time = Instant::now();
    
    // Perform an empty loop 1,000,000 times
    for _ in 0..1_000_000 {
        // This loop does nothing; it's used to measure the overhead of looping
    }
    
    let end_time = start_time.elapsed();
    println!("Loop took {:?}", end_time);
}

pub fn test_function_call() {
    // Define a dummy function that does nothing
    fn dummy_function() {}
    
    let start_time = Instant::now();
    
    // Call the dummy function 1,000,000 times
    for _ in 0..1_000_000 {
        dummy_function();
    }
    
    let end_time = start_time.elapsed();
    println!("Function call took {:?}", end_time);
}

fn main() {
    test_list_operations();
    test_dict_operations();
    test_loop();
    test_function_call();
}

Output

List operations took 16.311708ms
Dictionary operations took 663.726µs
Loop took 8.946678ms
Function call took 9.223464ms

Python 3.12 code

import time  # Import the time module to measure elapsed time for operations
from collections import defaultdict  # Import defaultdict for dictionary operations (not used here but can be useful)

def test_list_operations():
    # Initialize a list with numbers from 0 to 99999
    lst = list(range(100000))
    # Record the start time for performance measurement
    start_time = time.time()
    # Perform a series of operations 1000 times
    for _ in range(1000):
        # Append the current length of the list to the end of the list
        lst.append(len(lst))
        # Remove the first element of the list (index 0)
        lst.pop(0)
    # Calculate the total elapsed time by subtracting start time from the current time
    end_time = time.time() - start_time
    # Print the time taken for list operations in seconds, formatted to 4 decimal places
    print(f"List operations took {end_time:.4f} seconds")

def test_dict_operations():
    # Initialize a dictionary with keys from 0 to 9999 and values as their squares
    dct = {i: i * i for i in range(10000)}
    # Add the key 0 with a value of 0 to the dictionary
    dct[0] = 0
    # Record the start time for performance measurement
    start_time = time.time()
    # Perform a series of operations 1000 times
    for _ in range(1000):
        # Insert a new key-value pair where the key is the current size of the dictionary
        dct[len(dct)] = len(dct) * len(dct)
        # Check if the key 0 exists in the dictionary
        if 0 in dct:
            # Remove the key 0 from the dictionary if it exists
            del dct[0]
    # Calculate the total elapsed time for dictionary operations
    end_time = time.time() - start_time
    # Print the time taken for dictionary operations in seconds
    print(f"Dictionary operations took {end_time:.4f} seconds")

def test_loop():
    # Record the start time for measuring the loop duration
    start_time = time.time()
    # Perform an empty loop 1,000,000 times
    for _ in range(1000000):
        pass  # This loop does nothing; it's used to measure the overhead of looping
    # Calculate the total elapsed time for the loop execution
    end_time = time.time() - start_time
    # Print the time taken for the empty loop in seconds
    print(f"Loop took {end_time:.4f} seconds")

def test_function_call():
    # Define a dummy function that does nothing
    def dummy_function():
        pass

    # Record the start time for measuring the function call duration
    start_time = time.time()
    # Call the dummy function 1,000,000 times
    for _ in range(1000000):
        dummy_function()  # Invoke the dummy function
    # Calculate the total elapsed time for the function calls
    end_time = time.time() - start_time
    # Print the time taken for function calls in seconds
    print(f"Function call took {end_time:.4f} seconds")

# Main execution block
if __name__ == "__main__":
    # Call each test function to measure and display their execution times
    test_list_operations()
    test_dict_operations()
    test_loop()
    test_function_call()

Output

List operations took 0.0145 seconds
Dictionary operations took 0.0004 seconds
Loop took 0.0887 seconds
Function call took 0.2298 seconds

First of all, let's change all the units to seconds so they can be compared equally:

               Python    Rust
List           0.0145 s  0.016311708 s
Dictionary     0.0004 s  0.000663726 s
Loop           0.0887 s  0.008946678 s
Function call  0.2298 s  0.009223464 s

It is not especially surprising that the list and dictionary operations are nearly the same, because in those cases the Python program is not running lots of interpreted Python operations — most of its time is spent executing the basic list and dictionary operations, which are implemented in C code.

If you want to speed up a Python program by replacing parts with Rust, then you have to replace some Python code that does many operations (likely some sort of loop over many items).

2 Likes

Thank you for your explanation. It seems that I should think more about the execution efficiency of python in C, rather than simply comparing the execution performance of Rust and python codes. This clarified my development direction in the project. :kissing_heart:

The trick is to run in release mode rather than in debug mode:

List operations took 16.441435ms
Dictionary operations took 32.34µs
Loop took 30ns
Function call took 30ns
4 Likes

Could you also tell us the trick how you had access to the OP's computer? :wink:

I once had a student of informatics working at our company, who didn't even know what the factor 'milli' meant. I assume that 'micro' and 'nano' is a huge challenge for some. Metric prefix - Wikipedia

And a final pro-tip: Remove all leading spaces of a python program to make it even faster. :slight_smile:

Be aware that it is almost certain that your loop is completely removed by the optimiser. It does nothing and so the compiler can refrain for generating any code for it at all without changing the observable results of the program.

Similarly for calling empty functions. The function that does nothing need not even be called and the loop that call is in does nothing so that can be dropped as well.

One has to be careful playing with such micro benchmarks. You are likely not measuring what you think you are measuring. You would. do better using Rust's built in benchmarking tool Benchmarking - The Rust Performance Book or using something like criterion: Criterion in criterion - Rust

It's better to benchmark code that does something and is some semblance of what you want to do in your application(s).

7 Likes

He uses Rust online testing as a standard, because it is a balance between hardware/OS/version/execution environment. Even if the same computer has different thread status, there will be different results. This should be a trade-off for code performance comparison. :face_with_raised_eyebrow:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.