Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Operating_Systems_Threads_and_Locking

Parallel Programming

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this homework on a real computer (not xv6, not qemu) that has multiple processors/cores (verify by running ‘cat /proc/cpuinfo’).

Copy the code tl_before_modification.c from the repository to your directory and compile it.

{yourpc:~} gcc -g -O2 tl.c -pthread
{yourpc:~} ./a.out 2

The argument 2 specifies the number of threads that execute put and get operations on the hash table.

After running for a little while, the program will produce output like this:

0: put time = 0.011398
1: put time = 0.011441
0: get time = 0.460219
0: 482 keys missing
1: get time = 0.464893
1: 476 keys missing
completion time = 0.476704

Each thread runs in two phases. In the first phase, each thread puts NKEYS/nthread keys into the hash table. In the second phase, each thread gets NKEYS/nthread from the hash table. The print statements tell you how long each phase took for each thread. The completion time at the bottom tells you the total runtime for the application. In the output above, the completion time for the application is about 0.476 seconds. Each thread computed for about 0.47 seconds (~0.01 for put + ~0.46 for get).

To see if using two threads improved performance, let us compare against a single thread:

{yourpc:~} ./a.out 1
0: put time = 0.012343
0: get time = 0.852030
0: 0 keys missing
completion time = 0.864743

The completion time for the single thread case (~0.86s) is slightly less than twice the two threads case. Thus, the two threads case achieved nearly 2x parallel speedup for the get phase on two processor/cores, which is very good.

  1. Why there is no speedup for put phase?

When you run this application, you may see no parallelism if you are running on a machine with only one core or if the machine is busy running other applications.

Two inferences: 1) The completion time for 2 threads is roughly half that of single thread; we are achieving good parallelism. 2) The output for two threads says that many keys are missing. In your runs, there may be more or fewer keys missing. If you run with one thread, there will never be any keys missing.

  1. Why are there missing keys with 2 or more threads, but not with 1 thread? Identify a sequence of events that can lead to keys missing for 2 threads.

To avoid this sequence of events, insert lock and unlock statements in put() and get() routines so that the number of keys missing is always 0. The relevant pthread calls are (for more see the manual pages, man pthread):

pthread_mutex_t lock; // declare a lock
pthread_mutex_init(&lock, NULL); // initialize the lock
pthread_mutex_lock(&lock); // acquire lock
pthread_mutex_unlock(&lock); // release lock

Test your code first with 1 thread, then test it with 2 threads. Is it correct (i.e. have you eliminated missing keys?)?

  1. Is the two-threaded version faster than the single-threaded version?

Modify your code so that get operations run in parallel while maintaining correctness. (Hint: are the locks in get necessary for correctness in this application?)

Modify your code so that some put operations run in parallel while maintaining correctness. (Hint: use a lock per bucket)

  1. What do you observe?
  2. What do you infer when you repeat the above experiments for more than 2 threads (say, 10 or more?)

Write your answers for the above 5 questions in a text file (name it e3.txt) and submit along with your code.

Make sure you comment the part of the code you added/modified.

About

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this on a computer with multiple cores (verify by running ‘cat /proc/cpuinfo’).

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Operating_Systems_Threads_and_Locking

Parallel Programming

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this homework on a real computer (not xv6, not qemu) that has multiple processors/cores (verify by running ‘cat /proc/cpuinfo’).

Copy the code tl_before_modification.c from the repository to your directory and compile it.

{yourpc:~} gcc -g -O2 tl.c -pthread
{yourpc:~} ./a.out 2

The argument 2 specifies the number of threads that execute put and get operations on the hash table.

After running for a little while, the program will produce output like this:

0: put time = 0.011398
1: put time = 0.011441
0: get time = 0.460219
0: 482 keys missing
1: get time = 0.464893
1: 476 keys missing
completion time = 0.476704

Each thread runs in two phases. In the first phase, each thread puts NKEYS/nthread keys into the hash table. In the second phase, each thread gets NKEYS/nthread from the hash table. The print statements tell you how long each phase took for each thread. The completion time at the bottom tells you the total runtime for the application. In the output above, the completion time for the application is about 0.476 seconds. Each thread computed for about 0.47 seconds (~0.01 for put + ~0.46 for get).

To see if using two threads improved performance, let us compare against a single thread:

{yourpc:~} ./a.out 1
0: put time = 0.012343
0: get time = 0.852030
0: 0 keys missing
completion time = 0.864743

The completion time for the single thread case (~0.86s) is slightly less than twice the two threads case. Thus, the two threads case achieved nearly 2x parallel speedup for the get phase on two processor/cores, which is very good.

  1. Why there is no speedup for put phase?

When you run this application, you may see no parallelism if you are running on a machine with only one core or if the machine is busy running other applications.

Two inferences: 1) The completion time for 2 threads is roughly half that of single thread; we are achieving good parallelism. 2) The output for two threads says that many keys are missing. In your runs, there may be more or fewer keys missing. If you run with one thread, there will never be any keys missing.

  1. Why are there missing keys with 2 or more threads, but not with 1 thread? Identify a sequence of events that can lead to keys missing for 2 threads.

To avoid this sequence of events, insert lock and unlock statements in put() and get() routines so that the number of keys missing is always 0. The relevant pthread calls are (for more see the manual pages, man pthread):

pthread_mutex_t lock; // declare a lock
pthread_mutex_init(&lock, NULL); // initialize the lock
pthread_mutex_lock(&lock); // acquire lock
pthread_mutex_unlock(&lock); // release lock

Test your code first with 1 thread, then test it with 2 threads. Is it correct (i.e. have you eliminated missing keys?)?

  1. Is the two-threaded version faster than the single-threaded version?

Modify your code so that get operations run in parallel while maintaining correctness. (Hint: are the locks in get necessary for correctness in this application?)

Modify your code so that some put operations run in parallel while maintaining correctness. (Hint: use a lock per bucket)

  1. What do you observe?
  2. What do you infer when you repeat the above experiments for more than 2 threads (say, 10 or more?)

Write your answers for the above 5 questions in a text file (name it e3.txt) and submit along with your code.

Make sure you comment the part of the code you added/modified.

About

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this on a computer with multiple cores (verify by running ‘cat /proc/cpuinfo’).

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Operating_Systems_Threads_and_Locking

Parallel Programming

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this homework on a real computer (not xv6, not qemu) that has multiple processors/cores (verify by running ‘cat /proc/cpuinfo’).

Copy the code tl_before_modification.c from the repository to your directory and compile it.

{yourpc:~} gcc -g -O2 tl.c -pthread
{yourpc:~} ./a.out 2

The argument 2 specifies the number of threads that execute put and get operations on the hash table.

After running for a little while, the program will produce output like this:

0: put time = 0.011398
1: put time = 0.011441
0: get time = 0.460219
0: 482 keys missing
1: get time = 0.464893
1: 476 keys missing
completion time = 0.476704

Each thread runs in two phases. In the first phase, each thread puts NKEYS/nthread keys into the hash table. In the second phase, each thread gets NKEYS/nthread from the hash table. The print statements tell you how long each phase took for each thread. The completion time at the bottom tells you the total runtime for the application. In the output above, the completion time for the application is about 0.476 seconds. Each thread computed for about 0.47 seconds (~0.01 for put + ~0.46 for get).

To see if using two threads improved performance, let us compare against a single thread:

{yourpc:~} ./a.out 1
0: put time = 0.012343
0: get time = 0.852030
0: 0 keys missing
completion time = 0.864743

The completion time for the single thread case (~0.86s) is slightly less than twice the two threads case. Thus, the two threads case achieved nearly 2x parallel speedup for the get phase on two processor/cores, which is very good.

  1. Why there is no speedup for put phase?

When you run this application, you may see no parallelism if you are running on a machine with only one core or if the machine is busy running other applications.

Two inferences: 1) The completion time for 2 threads is roughly half that of single thread; we are achieving good parallelism. 2) The output for two threads says that many keys are missing. In your runs, there may be more or fewer keys missing. If you run with one thread, there will never be any keys missing.

  1. Why are there missing keys with 2 or more threads, but not with 1 thread? Identify a sequence of events that can lead to keys missing for 2 threads.

To avoid this sequence of events, insert lock and unlock statements in put() and get() routines so that the number of keys missing is always 0. The relevant pthread calls are (for more see the manual pages, man pthread):

pthread_mutex_t lock; // declare a lock
pthread_mutex_init(&lock, NULL); // initialize the lock
pthread_mutex_lock(&lock); // acquire lock
pthread_mutex_unlock(&lock); // release lock

Test your code first with 1 thread, then test it with 2 threads. Is it correct (i.e. have you eliminated missing keys?)?

  1. Is the two-threaded version faster than the single-threaded version?

Modify your code so that get operations run in parallel while maintaining correctness. (Hint: are the locks in get necessary for correctness in this application?)

Modify your code so that some put operations run in parallel while maintaining correctness. (Hint: use a lock per bucket)

  1. What do you observe?
  2. What do you infer when you repeat the above experiments for more than 2 threads (say, 10 or more?)

Write your answers for the above 5 questions in a text file (name it e3.txt) and submit along with your code.

Make sure you comment the part of the code you added/modified.

About

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this on a computer with multiple cores (verify by running ‘cat /proc/cpuinfo’).

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Operating_Systems_Threads_and_Locking

Parallel Programming

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this homework on a real computer (not xv6, not qemu) that has multiple processors/cores (verify by running ‘cat /proc/cpuinfo’).

Copy the code tl_before_modification.c from the repository to your directory and compile it.

{yourpc:~} gcc -g -O2 tl.c -pthread
{yourpc:~} ./a.out 2

The argument 2 specifies the number of threads that execute put and get operations on the hash table.

After running for a little while, the program will produce output like this:

0: put time = 0.011398
1: put time = 0.011441
0: get time = 0.460219
0: 482 keys missing
1: get time = 0.464893
1: 476 keys missing
completion time = 0.476704

Each thread runs in two phases. In the first phase, each thread puts NKEYS/nthread keys into the hash table. In the second phase, each thread gets NKEYS/nthread from the hash table. The print statements tell you how long each phase took for each thread. The completion time at the bottom tells you the total runtime for the application. In the output above, the completion time for the application is about 0.476 seconds. Each thread computed for about 0.47 seconds (~0.01 for put + ~0.46 for get).

To see if using two threads improved performance, let us compare against a single thread:

{yourpc:~} ./a.out 1
0: put time = 0.012343
0: get time = 0.852030
0: 0 keys missing
completion time = 0.864743

The completion time for the single thread case (~0.86s) is slightly less than twice the two threads case. Thus, the two threads case achieved nearly 2x parallel speedup for the get phase on two processor/cores, which is very good.

  1. Why there is no speedup for put phase?

When you run this application, you may see no parallelism if you are running on a machine with only one core or if the machine is busy running other applications.

Two inferences: 1) The completion time for 2 threads is roughly half that of single thread; we are achieving good parallelism. 2) The output for two threads says that many keys are missing. In your runs, there may be more or fewer keys missing. If you run with one thread, there will never be any keys missing.

  1. Why are there missing keys with 2 or more threads, but not with 1 thread? Identify a sequence of events that can lead to keys missing for 2 threads.

To avoid this sequence of events, insert lock and unlock statements in put() and get() routines so that the number of keys missing is always 0. The relevant pthread calls are (for more see the manual pages, man pthread):

pthread_mutex_t lock; // declare a lock
pthread_mutex_init(&lock, NULL); // initialize the lock
pthread_mutex_lock(&lock); // acquire lock
pthread_mutex_unlock(&lock); // release lock

Test your code first with 1 thread, then test it with 2 threads. Is it correct (i.e. have you eliminated missing keys?)?

  1. Is the two-threaded version faster than the single-threaded version?

Modify your code so that get operations run in parallel while maintaining correctness. (Hint: are the locks in get necessary for correctness in this application?)

Modify your code so that some put operations run in parallel while maintaining correctness. (Hint: use a lock per bucket)

  1. What do you observe?
  2. What do you infer when you repeat the above experiments for more than 2 threads (say, 10 or more?)

Write your answers for the above 5 questions in a text file (name it e3.txt) and submit along with your code.

Make sure you comment the part of the code you added/modified.

About

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this on a computer with multiple cores (verify by running ‘cat /proc/cpuinfo’).

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Operating_Systems_Threads_and_Locking

Parallel Programming

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this homework on a real computer (not xv6, not qemu) that has multiple processors/cores (verify by running ‘cat /proc/cpuinfo’).

Copy the code tl_before_modification.c from the repository to your directory and compile it.

{yourpc:~} gcc -g -O2 tl.c -pthread
{yourpc:~} ./a.out 2

The argument 2 specifies the number of threads that execute put and get operations on the hash table.

After running for a little while, the program will produce output like this:

0: put time = 0.011398
1: put time = 0.011441
0: get time = 0.460219
0: 482 keys missing
1: get time = 0.464893
1: 476 keys missing
completion time = 0.476704

Each thread runs in two phases. In the first phase, each thread puts NKEYS/nthread keys into the hash table. In the second phase, each thread gets NKEYS/nthread from the hash table. The print statements tell you how long each phase took for each thread. The completion time at the bottom tells you the total runtime for the application. In the output above, the completion time for the application is about 0.476 seconds. Each thread computed for about 0.47 seconds (~0.01 for put + ~0.46 for get).

To see if using two threads improved performance, let us compare against a single thread:

{yourpc:~} ./a.out 1
0: put time = 0.012343
0: get time = 0.852030
0: 0 keys missing
completion time = 0.864743

The completion time for the single thread case (~0.86s) is slightly less than twice the two threads case. Thus, the two threads case achieved nearly 2x parallel speedup for the get phase on two processor/cores, which is very good.

  1. Why there is no speedup for put phase?

When you run this application, you may see no parallelism if you are running on a machine with only one core or if the machine is busy running other applications.

Two inferences: 1) The completion time for 2 threads is roughly half that of single thread; we are achieving good parallelism. 2) The output for two threads says that many keys are missing. In your runs, there may be more or fewer keys missing. If you run with one thread, there will never be any keys missing.

  1. Why are there missing keys with 2 or more threads, but not with 1 thread? Identify a sequence of events that can lead to keys missing for 2 threads.

To avoid this sequence of events, insert lock and unlock statements in put() and get() routines so that the number of keys missing is always 0. The relevant pthread calls are (for more see the manual pages, man pthread):

pthread_mutex_t lock; // declare a lock
pthread_mutex_init(&lock, NULL); // initialize the lock
pthread_mutex_lock(&lock); // acquire lock
pthread_mutex_unlock(&lock); // release lock

Test your code first with 1 thread, then test it with 2 threads. Is it correct (i.e. have you eliminated missing keys?)?

  1. Is the two-threaded version faster than the single-threaded version?

Modify your code so that get operations run in parallel while maintaining correctness. (Hint: are the locks in get necessary for correctness in this application?)

Modify your code so that some put operations run in parallel while maintaining correctness. (Hint: use a lock per bucket)

  1. What do you observe?
  2. What do you infer when you repeat the above experiments for more than 2 threads (say, 10 or more?)

Write your answers for the above 5 questions in a text file (name it e3.txt) and submit along with your code.

Make sure you comment the part of the code you added/modified.

About

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this on a computer with multiple cores (verify by running ‘cat /proc/cpuinfo’).

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Operating_Systems_Threads_and_Locking

Parallel Programming

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this homework on a real computer (not xv6, not qemu) that has multiple processors/cores (verify by running ‘cat /proc/cpuinfo’).

Copy the code tl_before_modification.c from the repository to your directory and compile it.

{yourpc:~} gcc -g -O2 tl.c -pthread
{yourpc:~} ./a.out 2

The argument 2 specifies the number of threads that execute put and get operations on the hash table.

After running for a little while, the program will produce output like this:

0: put time = 0.011398
1: put time = 0.011441
0: get time = 0.460219
0: 482 keys missing
1: get time = 0.464893
1: 476 keys missing
completion time = 0.476704

Each thread runs in two phases. In the first phase, each thread puts NKEYS/nthread keys into the hash table. In the second phase, each thread gets NKEYS/nthread from the hash table. The print statements tell you how long each phase took for each thread. The completion time at the bottom tells you the total runtime for the application. In the output above, the completion time for the application is about 0.476 seconds. Each thread computed for about 0.47 seconds (~0.01 for put + ~0.46 for get).

To see if using two threads improved performance, let us compare against a single thread:

{yourpc:~} ./a.out 1
0: put time = 0.012343
0: get time = 0.852030
0: 0 keys missing
completion time = 0.864743

The completion time for the single thread case (~0.86s) is slightly less than twice the two threads case. Thus, the two threads case achieved nearly 2x parallel speedup for the get phase on two processor/cores, which is very good.

  1. Why there is no speedup for put phase?

When you run this application, you may see no parallelism if you are running on a machine with only one core or if the machine is busy running other applications.

Two inferences: 1) The completion time for 2 threads is roughly half that of single thread; we are achieving good parallelism. 2) The output for two threads says that many keys are missing. In your runs, there may be more or fewer keys missing. If you run with one thread, there will never be any keys missing.

  1. Why are there missing keys with 2 or more threads, but not with 1 thread? Identify a sequence of events that can lead to keys missing for 2 threads.

To avoid this sequence of events, insert lock and unlock statements in put() and get() routines so that the number of keys missing is always 0. The relevant pthread calls are (for more see the manual pages, man pthread):

pthread_mutex_t lock; // declare a lock
pthread_mutex_init(&lock, NULL); // initialize the lock
pthread_mutex_lock(&lock); // acquire lock
pthread_mutex_unlock(&lock); // release lock

Test your code first with 1 thread, then test it with 2 threads. Is it correct (i.e. have you eliminated missing keys?)?

  1. Is the two-threaded version faster than the single-threaded version?

Modify your code so that get operations run in parallel while maintaining correctness. (Hint: are the locks in get necessary for correctness in this application?)

Modify your code so that some put operations run in parallel while maintaining correctness. (Hint: use a lock per bucket)

  1. What do you observe?
  2. What do you infer when you repeat the above experiments for more than 2 threads (say, 10 or more?)

Write your answers for the above 5 questions in a text file (name it e3.txt) and submit along with your code.

Make sure you comment the part of the code you added/modified.

About

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this on a computer with multiple cores (verify by running ‘cat /proc/cpuinfo’).

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Operating_Systems_Threads_and_Locking

Parallel Programming

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this homework on a real computer (not xv6, not qemu) that has multiple processors/cores (verify by running ‘cat /proc/cpuinfo’).

Copy the code tl_before_modification.c from the repository to your directory and compile it.

{yourpc:~} gcc -g -O2 tl.c -pthread
{yourpc:~} ./a.out 2

The argument 2 specifies the number of threads that execute put and get operations on the hash table.

After running for a little while, the program will produce output like this:

0: put time = 0.011398
1: put time = 0.011441
0: get time = 0.460219
0: 482 keys missing
1: get time = 0.464893
1: 476 keys missing
completion time = 0.476704

Each thread runs in two phases. In the first phase, each thread puts NKEYS/nthread keys into the hash table. In the second phase, each thread gets NKEYS/nthread from the hash table. The print statements tell you how long each phase took for each thread. The completion time at the bottom tells you the total runtime for the application. In the output above, the completion time for the application is about 0.476 seconds. Each thread computed for about 0.47 seconds (~0.01 for put + ~0.46 for get).

To see if using two threads improved performance, let us compare against a single thread:

{yourpc:~} ./a.out 1
0: put time = 0.012343
0: get time = 0.852030
0: 0 keys missing
completion time = 0.864743

The completion time for the single thread case (~0.86s) is slightly less than twice the two threads case. Thus, the two threads case achieved nearly 2x parallel speedup for the get phase on two processor/cores, which is very good.

  1. Why there is no speedup for put phase?

When you run this application, you may see no parallelism if you are running on a machine with only one core or if the machine is busy running other applications.

Two inferences: 1) The completion time for 2 threads is roughly half that of single thread; we are achieving good parallelism. 2) The output for two threads says that many keys are missing. In your runs, there may be more or fewer keys missing. If you run with one thread, there will never be any keys missing.

  1. Why are there missing keys with 2 or more threads, but not with 1 thread? Identify a sequence of events that can lead to keys missing for 2 threads.

To avoid this sequence of events, insert lock and unlock statements in put() and get() routines so that the number of keys missing is always 0. The relevant pthread calls are (for more see the manual pages, man pthread):

pthread_mutex_t lock; // declare a lock
pthread_mutex_init(&lock, NULL); // initialize the lock
pthread_mutex_lock(&lock); // acquire lock
pthread_mutex_unlock(&lock); // release lock

Test your code first with 1 thread, then test it with 2 threads. Is it correct (i.e. have you eliminated missing keys?)?

  1. Is the two-threaded version faster than the single-threaded version?

Modify your code so that get operations run in parallel while maintaining correctness. (Hint: are the locks in get necessary for correctness in this application?)

Modify your code so that some put operations run in parallel while maintaining correctness. (Hint: use a lock per bucket)

  1. What do you observe?
  2. What do you infer when you repeat the above experiments for more than 2 threads (say, 10 or more?)

Write your answers for the above 5 questions in a text file (name it e3.txt) and submit along with your code.

Make sure you comment the part of the code you added/modified.

About

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this on a computer with multiple cores (verify by running ‘cat /proc/cpuinfo’).

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Operating_Systems_Threads_and_Locking

Parallel Programming

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this homework on a real computer (not xv6, not qemu) that has multiple processors/cores (verify by running ‘cat /proc/cpuinfo’).

Copy the code tl_before_modification.c from the repository to your directory and compile it.

{yourpc:~} gcc -g -O2 tl.c -pthread
{yourpc:~} ./a.out 2

The argument 2 specifies the number of threads that execute put and get operations on the hash table.

After running for a little while, the program will produce output like this:

0: put time = 0.011398
1: put time = 0.011441
0: get time = 0.460219
0: 482 keys missing
1: get time = 0.464893
1: 476 keys missing
completion time = 0.476704

Each thread runs in two phases. In the first phase, each thread puts NKEYS/nthread keys into the hash table. In the second phase, each thread gets NKEYS/nthread from the hash table. The print statements tell you how long each phase took for each thread. The completion time at the bottom tells you the total runtime for the application. In the output above, the completion time for the application is about 0.476 seconds. Each thread computed for about 0.47 seconds (~0.01 for put + ~0.46 for get).

To see if using two threads improved performance, let us compare against a single thread:

{yourpc:~} ./a.out 1
0: put time = 0.012343
0: get time = 0.852030
0: 0 keys missing
completion time = 0.864743

The completion time for the single thread case (~0.86s) is slightly less than twice the two threads case. Thus, the two threads case achieved nearly 2x parallel speedup for the get phase on two processor/cores, which is very good.

  1. Why there is no speedup for put phase?

When you run this application, you may see no parallelism if you are running on a machine with only one core or if the machine is busy running other applications.

Two inferences: 1) The completion time for 2 threads is roughly half that of single thread; we are achieving good parallelism. 2) The output for two threads says that many keys are missing. In your runs, there may be more or fewer keys missing. If you run with one thread, there will never be any keys missing.

  1. Why are there missing keys with 2 or more threads, but not with 1 thread? Identify a sequence of events that can lead to keys missing for 2 threads.

To avoid this sequence of events, insert lock and unlock statements in put() and get() routines so that the number of keys missing is always 0. The relevant pthread calls are (for more see the manual pages, man pthread):

pthread_mutex_t lock; // declare a lock
pthread_mutex_init(&lock, NULL); // initialize the lock
pthread_mutex_lock(&lock); // acquire lock
pthread_mutex_unlock(&lock); // release lock

Test your code first with 1 thread, then test it with 2 threads. Is it correct (i.e. have you eliminated missing keys?)?

  1. Is the two-threaded version faster than the single-threaded version?

Modify your code so that get operations run in parallel while maintaining correctness. (Hint: are the locks in get necessary for correctness in this application?)

Modify your code so that some put operations run in parallel while maintaining correctness. (Hint: use a lock per bucket)

  1. What do you observe?
  2. What do you infer when you repeat the above experiments for more than 2 threads (say, 10 or more?)

Write your answers for the above 5 questions in a text file (name it e3.txt) and submit along with your code.

Make sure you comment the part of the code you added/modified.

About

In this exercise, you will explore parallel programming with threads and locks using a hash table. You should do this on a computer with multiple cores (verify by running ‘cat /proc/cpuinfo’).

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages