Tid: 08:15 - 13:15
a) Vad innebär en monolitisk kärna?
b) Vad innebär en mikrokärna?
c) Vilka fördelar och nackdelar har de två?
d) Jag skrev ovan att man numera oftast kombinerar dem. Hur går det till? Vilka fördelar ger det?
a) Hur mycket fysiskt minne kan man adressera? Visa också hur du räknat.
b) Hur mycket virtuellt minne kan man adressera? Visa också hur du räknat.
c) Hur stora är minnessidorna? Visa också hur du räknat.
d) Hade det varit rimligt att ha en offsetdel som var 12 bitar i stället för 13? Motivera svaret.
e) Hade det varit rimligt att ha en offset som var 3 bitar? Motivera svaret.
f) Hade det varit rimligt att ha en offset som var 34 bitar? Motivera svaret.
Vi antar att alla provkörningar i den här uppgiften görs på en normal, modern dator. Det kan vara en stationär dator eller en bärbar dator. Det är tillåtet att provköra på riktigt när man löser uppgiften, men det går att besvara alla frågorna även utan att provköra. Man måste i vilket fall som helst förklara sina svar. Det är förklaringarna som är det viktiga. |
// Many threads, each incrementing the same variable many times // Note 1: Normal "int" is too small for some of the values, // so we use "long long int". // Note 2: An optimizing compiler might remove the actual calculations, // so don't compile with -O2 or similar. // Thomas Padron-McCarthy (thomas.padron-mccarthy@oru.se) // Fri Aug 21 10:22:16 CEST 2020 #include <stdio.h> #include <stdlib.h> #include <limits.h> #include <pthread.h> #define TOTAL_NR_INCREMENTS (1LL*1000*1000*1000) #define NR_THREADS 100 #define NR_INCREMENTS_PER_THREAD (TOTAL_NR_INCREMENTS / NR_THREADS) pthread_t threads[NR_THREADS]; long long int the_big_number = 0; void *thread_body(void *arg) { int thread_number = (int)arg; for (long long int i = 0; i < NR_INCREMENTS_PER_THREAD; ++i) { the_big_number ++; } return NULL; } // thread_body int main(void) { printf("This program increments the same variable %lld times,\n", TOTAL_NR_INCREMENTS); printf("using %d thread(s), each incrementing the variable %lld times.\n", NR_THREADS, NR_INCREMENTS_PER_THREAD); printf("Starting the %d threads...\n", NR_THREADS); for (int i = 0; i < NR_THREADS; ++i) { if (pthread_create(&threads[i], NULL, thread_body, (void*)i) != 0) { printf("Couldn't create thread %d.\n", i); exit(EXIT_FAILURE); } } printf("%d threads started. Waiting for them to finish...\n", NR_THREADS); for (int i = 0; i < NR_THREADS; ++i) { if (pthread_join(threads[i], NULL) != 0) { printf("Couldn't join thread for thread %d.\n", i); exit(EXIT_FAILURE); } } printf("All threads finished!\n"); printf("The variable now has the value = %lld.\n", the_big_number); printf("It should be %lld.\n", TOTAL_NR_INCREMENTS); if (the_big_number == TOTAL_NR_INCREMENTS) printf("Ok! As expected!\n"); else printf("Error! Wrong result!\n"); return EXIT_SUCCESS; } // main |
a) Vi kör programmet. Det tar 2,3 sekunder, men variabelns värde blir inte alls 100 miljarder, utan 37226719. Vad beror det på att det blir fel värde?
b) Vi kör programmet en gång till. Det tar 2,3 sekunder den här gången också, men nu blir variabelns värde 39579883, alltså ett annat värde än förra gången. Vad beror det på att det blir ett annat värde?
c) Vi ändrar konstanten NR_THREADS till 1, så det bara körs en enda tråd, och kör programmet ännu en gång. Vad får variabeln för värde nu? Varför?
d) Vi ändrar tillbaka konstanten NR_THREADS till 100. Ändra programmet så det ger rätt resultat, även med 100 parallella trådar! (Det är som sagt tillåtet att provköra, men det räcker med att visa vilka rader som behöver läggas till i programmet.)
e) Vi kör programmet från deluppgift d ovan. Om du gjort rätt får variabeln det förväntade värdet. Men ungefär hur lång tid kan man räkna med att det tar att köra programmet? Varför?
f) Jag vill att programmet ska gå fortare att köra, så jag ändrar konstanten NR_THREADS till 1000. Vad händer med körtiden jämfört med när det var 100 trådar? Varför?