Performance Evaluation For Choosing Rust and C++
Performance Evaluation For Choosing Rust and C++
05 2023
Patrik Karlsson
The authors declare that they are the sole authors of this thesis and that they have not used
any sources other than those listed in the bibliography and identified as references. They further
declare that they have not submitted this thesis at any other institution to obtain a degree.
Contact Information:
Author(s):
Patrik Karlsson
E-mail: [email protected]
University advisor:
Associate professor Emil Alégroth
Department of Software Engineering
Developers face numerous challenges in their careers, including the critical decision
of choosing the most suitable programming language to tackle these challenges. Each
programming language presents its unique set of advantages and disadvantages, mak-
ing the decision-making process complex. This study focuses on one such decision –
the selection between Rust and C++ which are both systems programming languages
with significant emphasis on performance.
Rust, an emerging and increasingly popular language, offers a compelling alter-
native to the more established C++. To aid practitioners in making an informed
decision, this study explores the performance differences between Rust and C++
through three distinct experiments: matrix multiplication, merge sort, and file I/O
operations.
The experiments reveal that C++ demonstrates significantly faster performance
in matrix multiplication. Conversely, Rust showcases superior performance in merge
sort, with both languages performing similarly overall. The findings pertaining to file
operations were mixed, with C++ exhibiting shorter execution times for file reading,
while Rust displayed an advantage in writing larger file sizes.
By shedding light on these performance disparities, this study aims to assist
developers in their decision-making process when selecting between Rust and C++.
I would like to thank my advisor Emil for his fast and accurate guidance when writing
the study.
iii
Contents
Abstract i
Acknowledgments iii
1 Introduction 1
1.1 Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Rust . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.2 C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.3 Similarities and differences . . . . . . . . . . . . . . . . . . . . 3
1.1.4 The importance of finding new alternatives . . . . . . . . . . . 3
1.1.5 Goal of study . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.1 Aim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2.2 Objective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 Outline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2 Related Work 7
3 Method 9
3.1 Research questions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.2 Research methodology . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.2.1 Matrix multiplication experiment . . . . . . . . . . . . . . . . 10
3.2.2 Merge sort experiment . . . . . . . . . . . . . . . . . . . . . . 10
3.2.3 Read and writing experiment . . . . . . . . . . . . . . . . . . 10
3.3 Validity and reliability of your approach . . . . . . . . . . . . . . . . 10
3.3.1 Measurement . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.3.2 Further discussion of validity and replication . . . . . . . . . . 11
3.3.3 Motivation and relevancy of experiments . . . . . . . . . . . . 11
v
5 Discussion 23
5.1 Matrix Multiplication . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.2 Merge sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
5.3 Reading and Writing . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5.4 Ethical, Societal and Sustainability concerns . . . . . . . . . . . . . . 26
References 29
vi
Chapter 1
Introduction
1.1 Background
In software engineering, developers are faced with a multitude of decisions, and one
of the earliest and most impactful choices is selecting the programming language
for development. There are many choices and all have different capabilities such
as performance, safety, and more. One of the newer alternatives is the low-level
programming language Rust which has only grown more and more popular since its
release.
1.1.1 Rust
Rust is a low-level programming language that focuses on safety, speed, and con-
currency according to Mozilla [18]. To achieve this, Rust is statically typed and is
considered memory safe despite lacking a garbage collector. Since Rust can ensure
memory safety without relying on a garbage collector, it avoids the performance
overhead typically associated with garbage collection. This enables Rust to gain a
performance advantage over languages that rely on garbage collection for memory
management.
1
2 Chapter 1. Introduction
1.1.2 C++
While Rust has gained significant popularity and offers unique capabilities, it is
important to acknowledge that there are indeed older and more well-known alterna-
tives available that may possess similar capabilities in certain areas. For example,
one such alternative is C++. C++ is a mature and widely adopted programming
language that has been in existence for several decades. It provides low-level func-
tionality, supports object-oriented programming, and offers a high degree of control
over system resources and memory management. These features make it suitable for
developing high-performance applications, similar to Rust.
1.2 Scope
1.2.1 Aim
The aim of this study is to provide information for practitioners about the perfor-
mance differences between C++ and Rust so they can make a more informed decision
when choosing between developing in either Rust or C++.
1.2.2 Objective
The objective of this study is to conduct a performance comparison between Rust
and C++ across various tasks. These tasks involve implementing and evaluating al-
gorithms that have been used in previous research to compare different programming
1.3. Outline 5
languages. Examples of prior work that utilizes these algorithms include the study
by Wong, who used matrix multiplication to assess the performance of C++ [29].
Another example is given by Zhang et al., where the researchers used smaller
programs such as sorting algorithms to measure the overhead of Rust [30]. Yet
another example is the study by Sharma, where the performance of Java and C++
were compared on sorting numbers and writing and reading files [22].
In this study, two of the chosen applications are matrix multiplication and sorting
numbers in a list. The chosen algorithms, including matrix multiplication, sorting,
and file read/write operations, are well-suited to demonstrate the efficiency of CPU
and memory utilization in both Rust and C++. These applications provide insights
into how the languages handle computational tasks and disk operations, allowing for
a comprehensive performance evaluation.
The study by Zhang et al., justifies the comparison of programming languages
with microprograms, which is small applications and algorithms [30]. This approach
ensures a fair and unbiased performance comparison. Microprograms are consid-
ered fair because they focus on the fundamental constructs and operations of the
languages, this ensures that any differences are due to the language and not imple-
mentation differences such as different algorithms used.
In this study, the focus will be placed solely on the metric of execution time, which
will be consistently measured across all experiments. The decision to use execution
time as the primary metric is because this study aims to provide a standardized
measure that allows for straightforward comparisons and assessments across different
experiments. This approach ensures that the results can be easily compared and
combined, enabling practitioners to gauge the relative performance of C++ and
Rust in various scenarios.
1.3 Outline
This study will serve as a decision support tool, providing valuable information to
practitioners to make more informed decisions regarding their choice of programming
language between Rust and C++. The findings and insights gained from this study
will be presented in the Results and Analysis chapter. This presentation will be
through description, tables and images.
In the discussion chapter, there will be a discussion of the result and analysis
section. With this discussion, practitioners will be better equipped to select the most
suitable programming language that aligns with their specific use case requirements
and goals.
The methodology which will give practitioners this information will be through
experiments aiming to target large generalized areas such as scientific computing and
machine learning.
The experiments in this study will involve collecting data from matrix multiplica-
tion, merge sort, and reading/writing implementations developed in both C++ and
Rust. These implementations will be executed using multiple data sets to ensure a
comprehensive evaluation. The results will then be analysed. The analyzed results
will provide practitioners with valuable information for making informed decisions
regarding the choice of programming language, specifically focusing on performance.
Chapter 2
Related Work
Few studies researching Rust have been created and studies targeting Rust perfor-
mance are even rarer. But there is some previous work in this area. There is also
work doing similar comparisons but with other languages.
One notable study that shares similarities with this research is the work conducted
by Franzén and Östling [9]. In a manner similar to this study, they employed matrix
multiplication as a benchmark to compare the performance differences between C++
and Rust. In Franzén and Östling’s study, the matrix sizes chosen for evaluation
are similar to this study’s approach. They utilized matrix sizes of 32x32, 128x128,
512x512, and 2048x2048 to compare the performance of C++ and Rust. A significant
distinction between this study and Franzén and Östling’s research lies in their focus
on GPU comparison, whereas the present study does not emphasize this aspect. In
Franzén and Östling’s study, the main objective was to compare the performance of
C++ and Rust specifically in the context of GPU computing. In contrast, this study
aims to compare the overall performance of Rust and C++ across various tasks,
including matrix multiplication, sorting algorithms, and file read/write operations.
Another noteworthy study in the realm of Rust performance is documented in a
paper by Wang et al [16]. Their study specifically examines the overhead of Rust
compared to the C programming language. In the study conducted by Wang et al.,
a wide range of algorithms, particularly focusing on various sorting algorithms, were
investigated to evaluate the performance of Rust. Additionally, they incorporated
applications from the 22.05 computer benchmarks, which they referred to as game
benchmarks [10]. The study conducted by Wang et al. and the present study differs
in several aspects. Wang et al.’s study primarily focus on calculating the overhead
of the Rust programming language when compared to the C programming language.
Another difference between the study by Wang et.al and this study is that this study
compares the execution speed of applications instead of calculating the overhead of
the languages. Another significant distinction between the study by Wang et al., and
this study is the absence of specific tests targeting reading and writing files and does
not include a dedicated algorithm test for matrix multiplication either. In the study
conducted by Wang et al., they reported that Rust exhibited an average overhead of
1.77x compared to C.
A third comparison study was conducted by Medin a comparison was made be-
tween Rust compiled to WebAssembly (Wasm) and the C programming language [17].
Medin’s study utilized matrix multiplication, insertion sort, and an additional test
as benchmarks for the performance evaluation. In Medin’s study, the focus was
on evaluating the performance of Rust compiled to WebAssembly (Wasm) from a
7
8 Chapter 2. Related Work
web assembly perspective. Medin’s study also focused solely on the C program-
ming language. Regarding the matrix multiplication tests, Medin’s study utilized a
smaller data set size of 100x100. This data set size may not provide as comprehen-
sive insights into the performance characteristics as the larger data set sizes used
in this study, such as 250x250, 375x375, 500x500, 750x750, 1000x1000, 1250x1250,
1500x1500, 1750x1750, and 2000x2000. It’s worth mentioning that Medin’s study
had a focus on web assembly as well so it’s possible larger sizes were not of interest.
In Medin’s study, the results indicated that Rust, when compiled to WebAssembly
(Wasm) for web assembly purposes, exhibited faster performance compared to the
C programming language in the matrix multiplication and sorting algorithms. How-
ever, for the additional test performed in the study, Rust and C demonstrated similar
performance.
A fourth study is a study by Sharma, In Sharma’s study, the focus was on com-
paring the performance of Java and C++ programming languages [22]. Similar to
this study, Sharma’s research also included tasks involving reading and writing files
as well as sorting numbers to evaluate the execution speeds. The first major dif-
ference between this study and the study by Sharma is the programming language
difference. Another notable difference between Sharma’s study and this study is the
choice of algorithms for performance comparison. While this study includes matrix
multiplication and merge sort as part of the performance evaluation, Sharma’s study
did not utilize matrix multiplication and used a different sorting algorithm instead
of merge sort.
The data sets used in Sharma’s study were smaller compared to the data sets in
this study. Sharma’s study focused on number data sets with sizes of 1000, 10,000,
and 100,000 for reading and writing files as well as sorting algorithms. In contrast,
this study employed larger data sets for both reading and writing files and sorting
algorithms.
For reading and writing files, this study used data set sizes of 10,000, 100,000,
1,000,000, 5,000,000, 10,000,000, 50,000,000, 100,000,000, and 1,000,000,000 num-
bers. These larger data sets provide a more extensive evaluation of the languages’
performance in file operations, especially when dealing with larger volumes of data.
Similarly, for sorting algorithms, this study employed data set sizes of 100,000,
500,000, 1,000,000, 5,000,000, 10,000,000, 50,000,000, 100,000,000, and 200,000,000
numbers. By using larger data sets and more varied, this study enables a more
detailed analysis of the languages’ performance in sorting tasks, particularly when
handling significantly larger input sizes.
The inclusion of larger data sets in this study allows for a more comprehensive
assessment of the performance characteristics of Rust and C++ when working with
larger data volumes. This provides practitioners with valuable insights into how
these languages perform in scenarios involving reading and writing files as well as
sorting larger data sets, assisting them in making informed decisions when selecting a
programming language for their specific application requirements. With these values,
models will be created for each language and the models can be used to predict values
outside the test.
Chapter 3
Method
9
10 Chapter 3. Method
In general, the C++ applications in the study exhibited lower execution times com-
pared to the Rust applications. Although both languages demonstrated similar per-
formance when handling small data sizes, C++ outperformed Rust in a majority of
the experiments.
In the study by Franzén and Östling [9] the result from their matrix multiplica-
tion experiment also showed as this study results have done that C++ had a faster
execution speed compared to Rust but the results from this study suggested that
C++ had an even faster execution speed when it comes to matrix multiplication
than what Franzéns and Östlings study showed. Although there were differences
that definitely contributed to these differences such as being run with CPU in this
study as well as environmental differences and different data sets.
In the study by Medin [17] the result where reverse and Rust had a faster execu-
tion speed although this was from a web perspective when Rust and C were compiled
into web assembly which is a very different use case to this study and this different
environment definitely affected the result and the study by Medin also only used size
100 and it’s unclear if the results would be the same at larger sizes.
The results of this study are also similar to the results in the study by Zhang et
al., [30], where the merge sort result also was close to the C result but C had better
overhead. Although the study by Zhang et al., focused on calculating overhead and
also worked with C which of course is very similar to C++ the results are still clear
that with merge sort Rust has an extremely similar performance to C/C++. The
average overhead for Rust compared to C was 1,77. The results of this study did
not show as big of an advantage in performance as the results from Zhang et al.,
study this may have been partly because of the differences in what was being tested
such as language and metrics but also because the algorithms may have been more
beneficial for the execution time of C.
Generally, previous work showed that C++ had a faster execution speed than
Rust and in this study the result is similar. Most of the tests had a faster execution
speed for C++ but Rust had some cases where it had a faster execution speed such
as merge sort when it was faster although the results were similar. Writing was also
faster at large sizes for Rust.
13
14 Chapter 4. Results and Analysis
was 3000, where the C++ version exhibited an approximate execution time of 107
seconds, around 9% faster than the equivalent Rust version.
Although Rust avoids many of the runtime checks that garbage collectors need
the performance disparities between the two languages can be attributed, at least
in part, to the presence of run-time checks. These run-time checks, such as bounds
checks are inherent to Rust’s design and can impact its performance. The study by
Zhang et al., [30] supports this notion by discussing how run-time checks can influence
the efficiency of Rust applications. In this experiment, the bound check can affect
the performance significantly for this experiment due to for every multiplication the
arrays have to be bound-checked.
Another aspect that is important to understand is what takes time for this matrix
multiplication. Matrix multiplication of course includes a computation and this
computation have to have data from both input arrays this can be fetched much
more quickly if there is a good cache locality. Cache locality as mentioned in the
study by Lam et al., [13] explained how caching could improve the performance of
matrix multiplication. Optimizations that the languages or the compilers have done
may have affected the cache locality and therefore the results. After the data has
been fetched either from cache, memory or disk the computations can be done and
the result of the computation can be written to the results matrix.
In the Rust implementation of the matrix multiplication code, there is also a
potential issue related to the use of different integer types, namely i32 and i64. This
difference in types can lead to a type mismatch problem during indexing operations.
Specifically, when accessing the values of the first matrix and the second matrix slices,
Rust expects them to have the same integer type, either i32 or i64. Attempting to
index one slice with an integer of the wrong type will result in a compilation error
in Rust.
To avoid the type difference problem in the Rust version of the matrix multiplica-
tion code, the decision was made to use i64 consistently throughout the code instead
of mixing i32 and i64 types. By using i64 as the integer type consistently, there is no
need to cast or convert between different types when performing the multiplication
operation. The reason this might affect performance is due to do potentially more
memory was needed to store the matrices.
In the C++ version of the code, there is no explicit need for type casts when per-
forming arithmetic operations involving different integer types. Unlike Rust, C++
automatically promotes smaller integer types to larger ones when necessary. This
automatic type promotion ensures that the arithmetic operations are performed cor-
rectly without requiring explicit type casts.
As depicted in Figure 4.1, the performance differences between the C++ and Rust
implementations of matrix multiplication are increasing as the sizes of the matrices
grow larger. This indicates that the C++ version consistently outperforms the Rust
version with increasing matrix sizes.
However, it’s noteworthy to mention that the result of the last test involving a
matrix size of 2000 suggests a trend of the execution speeds becoming more similar
as the sizes grow larger. This observation could indicate that the performance gap
between the two languages narrows down as the matrix sizes increase.
4.2. Merge sort results 15
strated better performance. The execution time for writing speed grew linearly, but
it was evident that the Rust version had a slower rate of growth. Notably, for the
smallest data size, Rust was faster, but for all larger sizes except the two largest,
C++ performed better.
In the case of reading and writing experiments, it is difficult to pinpoint specific
reasons for the performance differences. Various factors could contribute to the
variations observed. For example, in Rust, the use of specific methods such as directly
reading file content into a string or utilizing the write-all function for writing could
have influenced the results. However, the exact factors responsible for the differences
are not clearly identifiable.
Matrix Multiplication
Number Count C++ ms Rust ms
250 6.52 9.62
375 26.22 33.44
500 61.70 79,61
750 241.23 312.56
1000 588.57 789.46
1250 1220.42 2285.54
1500 2274.75 4365.26
1750 5147.99 9880.91
2000 14030,35 21019.84
2500 44515.48 53386.83
3000 107293.55 118253.86
Merge sort
Number Count C++ ms Rust ms
105 9,74 9,22
5 ∗ 105 50,36 49,80
106 98,54 97,78
5 ∗ 106 504,12 502,33
107 1038,8 1030,17
5 ∗ 107 5430,97 5380,23
108 10885,61 10822,97
2*108 22334,26 21815,12
Read
Number Count C++ ms Rust ms
104 0,01 0,01
105 0,02 0,03
106 0,12 0,31
5 ∗ 106 1,02 1,74
107 2,84 2,92
5 ∗ 107 8,54 21,6
108 17,11 41,56
109 172,36 345,29
Write
Number Count C++ ms Rust ms
104 0,38 0,28
105 0,40 0,88
106 1,04 8,16
5 ∗ 106 7,39 20,79
107 20,88 83,18
5 ∗ 107 71,26 305,57
108 764,94 592,73
109 7799,02 5881,87
compilers and that both languages have an unpredictable execution time at larger
sizes of matrix multiplication.
According to the prediction Rust should have had a faster execution time than
C++ at size 3000 which did not happen but the % difference has definitely decreased
compared to smaller sizes, so if this continues Rust will perform faster but at which
size this could happen is not clear from the results from this study.
ms. This resulted in an approximation error of 3.12 ms, indicating that the predicted
result was approximately 0.2% faster than the actual execution time.
The purple dot represents the predicted value of 109 for Rust in terms of execution
time. When the code was tested, the measured execution time was 345.29 ms. This
resulted in an approximation error of 75.51 ms, indicating that the predicted result
was approximately 22% slower than the actual execution time for that size. However,
it’s important to note that the error of 75 ms can be attributed to the natural variance
in the execution time when running these applications.
The red dashed line represents the direct connection of all the experiment val-
ues without using the regression model. Both the C++ and Rust lines overlapped
significantly, indicating a low approximation error.
However, the approximation line for Rust displayed a different pattern, with
the lines diverging into separate paths after 108 . This observation aligns with the
previous calculation for Rust, where the execution time for larger sizes deviated from
the predicted model.
23
24 Chapter 5. Discussion
weather prediction, where sophisticated numerical models and simulations are em-
ployed to forecast weather patterns and improve our understanding of atmospheric
processes. As further mentioned by Shiflet and Shiflet these scientific computing
often have extremely high performance requirements to process all the information
collected. If this takes too long process researchers are not able to work with this
data efficiently. If the performance is faster it will also enable researchers to run
more complex problems that would not be possible otherwise. This study showed
that C++ from a matrix multiplication perspective was faster this means that if the
scientific computing use case uses matrix multiplication C++ will save more time
compared to and enable more complex problems than Rust.
Matrix multiplication is a fundamental algorithm with wide-ranging applications
in various fields, including machine learning, scientific computing, computer graphics,
and more. As the study by Li et al., [14] matrix multiplication was considered to
be on of the most fundamental algorithm and the efficiency of matrix multiplication
determines the computational complexities of almost all numerical algorithms. As
demonstrated by the performance results in the study, C++ consistently exhibited
faster execution times compared to Rust for matrix multiplication.
As shown in figure 4.1 Rust overall had a more predictable and stable performance
for matrix multiplications although C++ had a faster performance the increase in
stability of Rust might sway the choice in Rust’s favour when the stability of execu-
tion speed is important.
and Rust in executing merge sort with a slight advantage, which will provide insights
for decision-making.
This means that for Scientific computing the language which is used to make sure
that the data is sorted does not matter a lot from a performance perspective and
other considerations can be considered for scientific computing.
While the study does not offer a definitive advantage for either C++ or Rust in
terms of merge sort execution, this means that other considerations outside of perfor-
mance are especially important here. Instead, other considerations, such as ease of
development, language features, community support, and compatibility with existing
codebases, have a more prominent role in making decisions for this experiment.
Figure 4.2 also shows that both C++ and Rust have predictable and stable ex-
ecution speeds for all sizes tested which suggests that for use in applications which
are in need of stable execution time, there is no clear preference either.
stable execution time, which means for use cases in need of predictable performance
for writing Rust provides that. As for C++, the predictions are very unstable and
unpredictable which would suggest that this would not be the ideal choice for a
predictable execution time.
Considering applications that involve both reading and writing large data blocks,
Rust will prove to be more efficient, particularly when writing to disk due to the
writing speed taking much longer than reading does for large datasets.
These results suggest that for applications where reading and writing to disk is a
bottleneck C++ will be the faster choice for smaller sizes until we get to sizes such
as 108 where Rust performs faster for writing which is a more time-consuming disk
operation than reading.
6.1 Conclusion
In conclusion, C++ had faster execution speeds for more tests compared to Rust,
but the results also suggest that Rust will have a performance advantage in certain
areas.
For the matrix multiplication, the study findings indicate that C++ exhibited
faster execution speed compared to Rust but when sizes grew Rust execution become
more similar to C++ as sizes grew larger than 2000*2000 and if this continues rust
will have a faster execution speed. Both implementations demonstrated exponential
growth in execution time as the size of the matrices increased. Although both of the
implementations also did not grow as fast as expected as the sizes grew larger which
suggests that both languages made some optimizations to avoid having as big of a
performance decrease.
This suggests that the performance difference between C++ and Rust will con-
tinue to widen as the size of the matrices expands. Therefore, for applications involv-
ing matrix multiplication, C++ is preferable in terms of execution speed, particularly
for larger matrix sizes.
In summary for merge sort, the study results indicate that Rust exhibited faster
execution speed for merge sort compared to C++ in all tested scenarios. However,
the performance differences between the two languages were not significant, with
the largest observed difference being only 5%. Consequently, it is challenging to
draw a definitive conclusion regarding the superiority of either language for merge
sort. While Rust demonstrated slightly better performance across all tested sizes,
the marginal advantage may not be substantial enough to make definitive statements
about the preferred language for merge sort.
In terms of reading files, C++ demonstrated better performance across all tested
sizes, except for the first size where both languages had similar execution times. It is
important to note that both applications exhibited fast reading speeds, with none of
the execution times exceeding one second, even for the largest sizes. When it comes
to writing, C++ outperformed Rust for the majority of the tested data set sizes.
However, for the largest two data set sizes, Rust exhibited faster execution times,
indicating an advantage for writing larger sizes in Rust.
Considering an application that involves both reading and writing large blocks
of data, the execution time of writing becomes the dominant factor. Therefore, the
choice of language would depend on whether the data size exceeds the threshold
where Rust becomes faster in writing. In such cases, Rust would be the preferred
27
28 Chapter 6. Conclusions and Future Work
29
30 References