
Memory management László Daróczy Homework 1 - Solution • Explanation - one possible solution #include <iostream> //Function to test primes bool is_prime(unsigned long _num) { for(unsigned int i=2;i<_num;i++) { if(_num%i==0) return false;//Divisor found } return true;//No divisor found } int main(int argc, char** argv) { //Read n unsigned long N=0; std::cout << "How many primes do you want? " << std::ends; std::cin >> N; //Search method unsigned long found=0; //Number of found primes unsigned long tested=2; //Tested number while(found<N) { if(is_prime(tested++)) { std::cout << "The " << ++found << "-th prime is:" << tested-1 << std::endl; } } //Return return 0; } Memory management • Memory management covers the allocation and deallocation of memory, decides where to allocate memory, the memory management scheme, etc. •Why is it important? ➡Memory is finite (more than you think!) ➡Memory operations require the most time •DDR3 2x8 GB 1600 MHz CL11 -> 100 EUR •DDR3 2x8 GB 1600 MHz CL7 -> 215 EUR ➡In parallel computing access to memory is even more important (race conditions!) ➡Special hardware have special requirements, e.g., GPGPU •Accessing global memory: 400-600 clock cycles! •Accessing in a specific pattern increases bandwidth 16-32x! Memory allocation • Memory allocation happens in two different ways in C++: ➡automatic allocation: C++ defines, when and how the object will be allocated and the memory freed ➡compiler has full control over the process ➡allocation is done in the stack ➡dynamic allocation: the user has to take care to allocate and free the necessary memory ➡developer has full control over process ➡allocation is done on the heap Best practice: - Most important rule of programming: do not overcomplicate things!!!! - if it works do not touch it!!!! Automatic allocation • Automatic allocation by example: ➡memory is allocated at the definition ➡memory is freed at the end of the scope //… { //code… Memory is created for the object int a; ~object is constructed //code… } End of scope: memory is freed ~object is destructed Automatic allocation • Notes: ➡variable is only visible and accessible in its scope ➡automatic allocation is done in the stack!!!! ➡stack is a limited small memory location for programs (e.g., 1 MB) - using too much automatic allocations can lead to crash due to stack overflow, e.g., void recursive(/*…*/) { double data[500]; //5000 byte stack //code… recursive(/*…*/); //Crash after 200 recursions //code… } Automatic allocation - arrays • Array: ➡continuous (guaranteed by standard) segment of the memory containing several instances of the same object ➡can be allocated automatically or dynamically ➡automatic allocation is done in the stack! ➡by automatic allocation size of the array has to be (compile-time) constant! double array1[20]; //20 double const int N=60; int N2=6; N6+=N; long array2[N]; //60 long numbers long array3[N2]; //ERROR: non-const size Operations on arrays/1 •The elements of the arrays can be accessed using indices: double array1[20]; //20 double array1[5]=7.0; //Change element double test=array1[7]; //Read element •You can read, write and access elements using the index operator Note: - C & C++ use indices starting from 0 - an array with 20 elements has index 0…19 - accessing element 20 results in undefined behavior! Computing the first moments • Write a simple program to compute the first (central) moments of a series ➡which reads a single integer n from the user ➡reads n double values from user input ➡computes the first 5 central moments of the series 1 N µ = E [(X E(X))n] [X E(X)]n n i − ⇡ N i − i=1 ➡writes the results to the screen X ➡test the program with n=5 and 1.1; 2.1; 3.1; 4.1; 5.1 ➡(Result: 3.1; 2; 0; 6.8; 0) Computing the first moments • Explanation #include <iostream> #include <math.h> int main(int argc, char** argv) { //Number of values int n; //Large array double series[2000]; //moments double moments[5]; //Read n std::cout << "Number of values in series: " << std::ends; std::cin >> n; //Read the values for (int i = 0; i < n; i++) { std::cout << "Value " << i + 1 << " in the series: " << std::ends; std::cin >> series[i]; } //To be continued Computing the first moments • Explanation //Compute the values for (int order = 0; order < 5; order++) { double sum = 0.0; for (int i = 0; i < n; i++) { if (order == 0) sum += series[i]; else sum += pow(series[i] - moments[0], order + 1); } //compute the average moments[order] = sum / n; } //Print the values for (int order = 0; order < 5; order++) std::cout << "Moment of order " << order + 1 << ": " << moments[order] << std::endl; //Return 0 return 0; } Computing the first moments • We do not get 0 as result for the moments due to the rounding errors! std::cout << "Moment of order " << order + 1 << ": " << moments[order] << std::endl; std::cout << "Moment of order " << order + 1 << ": " << (fabs(moments[order])<1E-10 ? 0. : moments[order]) << std::endl; Computing the first moments • We only get a few digits! #include <iostream> #include <math.h> #include <iomanip> int main(int argc, char** argv) { //Number of values int n; //Large array double series[2000]; //moments double moments[5]; //Read n std::cout << "Number of values in series: " << std::ends; std::cin >> n; //Use 12 digits if necessary std::cout << std::setprecision(12); Note: Stream manipulators • Input/output streams have a state (~state machine) • State can be changed using manipulators, e.g., ➡std::setprecision(…) set decimal precision ➡std::showpoint show decimal point ➡std::skipws skip whitespaces ➡std::left adjust output to left ➡std::right adjust output to right ➡std::uppercase generate uppercase letters ➡std::scientific use scientific floating-point notation ➡and many others… Memory & addresses • Each object is stored in the memory •The memory can be imagined as a (very) long street •On one side of the street there are land properties (e.g, 1 m2) Memory street 1121 Memory street 1122 Memory street 1123 •Each property has a unique address •There does not necessarily has to be something at a specific address (can be empty)! Memory & addresses •If you are in front of a house, you can read the address from the shield! •If you have a large house, you need several landpieces! •Only by knowing an address, you have no idea, what is there (or if anything is there!) ➡you have to visit & enter the piece of land to see what is there! ➡this costs time ➡if the property is not yours but you enter, the police will stop you! Memory & addresses • The memory works exactly like that! •The memory is a long stripe (=street) of bytes (=land properties) 0x7fff5a0ca97c 0x7fff5a0ca97d 0x7fff5a0ca97e •Each byte has a unique address •A memory location can store an object or can be free •The objects are the houses, the address is stored in a pointer •i.e., they point to a memory address Memory & addresses •If you know the object (int a=7;), you can get the address using the address operator (&a) •If you have a large object (e.g., long), you need several bytes for the storage char int8_t char long unsigned char •Only by knowing an address, you have no idea, what is there (or if anything is there!) ➡you have to use the indirection operator (*pointer) to get the value ➡this costs time! ➡if the property is not yours (e.g., memory location of different program), but you enter, the operation system will stop you (and crash your program)! Memory & addresses • Summarized: •When you have an object (e.g., int num=7;), it is stored somewhere in the memory ➡The object has a type (int), a name (num) and a value (7). •A pointer (p1) is a special variable, which stores the address of something (int * p1=&a;) ➡The pointer also has a name (p1), a type (int *) and a value (the address). ➡To get the value of the object stored at the address, use the indirection operator (*p1 will be 7). ➡To get the address of something, you can use the address operator (&a) ➡A pointer can also point to another pointer (int **). Memory & addresses • Example - address of local objects #include <iostream> int main(int argc, char** argv) { //Create local variables int a=0;//Value is 0 int b=1;//Value is 1 int * p1=&a;//Address of a int * p2=&b;//Address of b //Print address std::cout << p1 << std::endl; std::cout << p2 << std::endl; //Print value!! std::cout << *p1 << std::endl; std::cout << *p2 << std::endl; return 0; } 0x7fff5a0ca97c 0x7fff5a0ca978 0 1 Accessing invalid memory Note: • If you are using the indirection operator at a memory address, which does not belong to your program, the operation system will stop you and crash your software (Segmentation fault). Note: • If you are using the indirection operator, which belongs to your program, but not to the object you wanted to, you get strange errors (difficult to debug)! #include <iostream> int main(int argc, char** argv) { long a=100; char c=’s'; //Create local variables char * p1=(char*)(&a); //Oh no, I meant a!!! std::cout << *p1 << std::endl; //Print value at location return 0; } //Output: d???? Fun fact •Bug: error in the software •Debugging: finding and correcting the error • Origin of the term: •1940: a moth got stuck in the relay of the computer of Harvard •So they debugged the system… Note: void • void is a special type in C++ •void means something has no type! •Why is it good? ➡if your function does not return any value, it can return nothing! void print_number(int value) { std::cout << value << std::endl; return; //Optional!!! } ➡if you want to store a memory address, but it is not important, what is stored there ➡E.g.,when you copy num bytes from one memory location to another
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages43 Page
-
File Size-