Project #0 - C++ Primer

Do not post your project on a public Github repository.

Overview

All the programming projects this semester will be written on the BusTub database management system. This system is written in C++. To make sure that you have the necessary C++ background, you must complete a simple programming assignment to assess your knowledge of basic C++ features. You will not be given a grade for this project, but you must complete the project with a perfect score before being allowed to proceed in the course. Any student unable to complete this assignment before the deadline will be asked to drop the course.

All of the code in this programming assignment must be written in C++. The projects will be specifically written for C++17, but we have found that it is generally sufficient to know C++11. If you have not used C++ before, here are some resources to help:

If you are using VSCode, we recommend you to install CMake Tools, C/C++ Extension Pack and clangd. After that, follow this tutorial to learn how to use the visual debugger in VSCode: Debug a C++ project in VS Code.

If you are using CLion, we recommend you to follow this tutorial: CLion Debugger Fundamentals.

If you prefer to use gdb for debugging, there are many tutorials available to teach you how to use gdb. Here are some that we have found useful:

This is a single-person project that will be completed individually (i.e. no groups, no coding agents).

Project Specification

Consider the following scenario: you are building an in-memory registry for a popular web service that must quickly determine whether a request identifier has already been seen. The service receives a large stream of requests from many worker threads, so the registry must support fast insertion and lookup without using more memory than necessary. This is where a Robin Hood hash set comes in!

Robin Hood hashing is an open-addressing technique for hash tables. Each key has a home bucket, and collisions are resolved by probing successive buckets, wrapping around at the end of the table. When a newly inserted key has probed farther than the key currently occupying a bucket, it displaces that key and lets the displaced key continue probing. This distributes probe distances more evenly, reducing long lookup sequences. Robin Hood hashing is useful for memory-efficient in-memory indexes and other high-throughput membership-tracking workloads.

Your table is fixed-capacity: it must not resize. Deleted entries should become tombstones so that lookups continue through them, while later insertions may reuse their buckets. All table operations must be safe when called concurrently.

Parameters

The Robin Hood hash set is configured with the following parameters:

Tombstones

In open-addressed hash tables, deleting a key cannot simply make its bucket empty: doing so could cause a later lookup to stop before reaching a key displaced by the deleted entry. Instead, deletion leaves a tombstone, a special marker indicating that the bucket was previously occupied but no longer contains a live key. Lookups must probe past tombstones, while insertions can reuse their buckets.

Each bucket is in exactly one of three logical states: empty, occupied, or tombstone. An empty bucket has never been occupied since the table was created or last cleared; a lookup may stop at an empty bucket because its key cannot appear later in that probe sequence. An occupied bucket contains a live key. A tombstone contains no live key but must not terminate a lookup: continue probing through tombstones until you find the key, reach an empty bucket, or have examined every bucket in the fixed-capacity table.

For example, in a table of capacity 8, keys 6, 14, and 22 all have home bucket 6 and may occupy buckets 6, 7, and 0, respectively. After removing 14, bucket 7 becomes a tombstone. A lookup for 22 must continue past that tombstone and wrap around to bucket 0. A later insertion of the colliding key 30 may reuse the tombstone at bucket 7.

Robin Hood Hashing Walkthrough

Consider a table with capacity 4. Each key's home bucket is hash(key) % 4; for this example, assume the hash of each integer is the integer itself. This is only to make the walkthrough easy to follow: your implementation must use the supplied hash function. We write each entry as key (probe distance), where probe distance is the number of buckets between the key's home bucket and its current bucket.

First, insert 0. Its home bucket is 0, which is empty:

Bucket:  0      1      2      3
         0 (0)  empty  empty  empty

Next, insert 4. Its home bucket is also 0, so it probes to bucket 1 and is stored with probe distance 1:

Bucket:  0      1      2      3
         0 (0)  4 (1)  empty  empty

Now insert 1. Its home bucket is 1, but bucket 1 is occupied by 4, which has already probed farther than 1 has. Therefore, 1 continues to bucket 2:

Bucket:  0      1      2      3
         0 (0)  4 (1)  1 (1)  empty

Finally, insert 8, whose home bucket is 0. It probes past 0 and 4; when it reaches bucket 2, it has probe distance 2, while 1 has probe distance 1. Under the Robin Hood rule, 8 displaces 1, and 1 continues probing to bucket 3:

Bucket:  0      1      2      3
         0 (0)  4 (1)  8 (2)  1 (2)

This displacement gives the key with the longer probe sequence (8) the earlier bucket. Robin Hood hashing uses this rule to distribute probe distances more evenly and reduce long lookup sequences.

Resources

Instructions

You will have to complete the following task for this project:

Task #1

Implement a basic Robin Hood hash set data structure supporting insertion, deletion, and lookup.

In src/include/primer/robin_hood_hash_set.h and src/primer/robin_hood_hash_set.cpp, implement the following functions:

Feel free to design your helper functions to assist with the implementation.

Important Information

Setting Up Your Project Repository

If the below git concepts (e.g., repository, merge, pull, fork) do not make sense to you, please spend some time learning git first.

The course staff will create and manage your own PRIVATE repository and your own development branch. See Piazza for details. If you have previously forked the repository through the GitHub UI (by clicking Fork), PLEASE DO NOT PUSH ANY CODE TO YOUR PUBLIC FORKED REPOSITORY! Make sure your repository is PRIVATE before you git push any of your code.

If the instructor makes any changes to the code, you can merge the changes to your code by keeping your private repository connected to the CMU-DB master repository. Execute the following commands to add a remote source:

$ git remote add public https://github.com/cmu-db/bustub.git

You can then pull down the latest changes as needed during the semester:

$ git fetch public
$ git merge public/master

Setting Up Your Development Environment

First install the packages that BusTub requires:

# Linux
$ sudo build_support/packages.sh
# macOS
$ build_support/packages.sh

See the README for additional information on how to setup different OS environments.

To build the system from the commandline, execute the following commands:

$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
$ make -j`nproc`

We recommend always configuring CMake in debug mode. This will enable you to output debug messages and check for memory leaks (more on this in below sections).

Testing

You can test the individual components of this assignment using our testing framework. We use GTest for unit test cases. You can disable tests in GTest by adding a DISABLED_ prefix to the test name. To run the tests from the command-line:

$ cd build
$ make -j$(nproc) robin_hood_hash_set_test
$ ./test/robin_hood_hash_set_test

In this project, there are no hidden tests. In the future, the provided tests in the starter code are only a subset of the all the tests that we will use to evaluate and grade your project. You should write additional test cases on your own to check the complete functionality of your implementation.

Make sure that you remove the DISABLED_ prefix from the test names otherwise they will not run!

Formatting

Your code must follow the Google C++ Style Guide. We use Clang to automatically check the quality of your source code. Your project grade will be zero if your submission fails any of these checks.

Execute the following commands to check your syntax. The format target will automatically correct your code. The check-lint and check-clang-tidy targets will print errors that you must manually fix to conform to our style guide.

$ make format
$ make check-lint
$ make check-clang-tidy-p0

Memory Leaks

For this project, we use LLVM Address Sanitizer (ASAN) and Leak Sanitizer (LSAN) to check for memory errors. To enable ASAN and LSAN, configure CMake in debug mode and run tests as you normally would. If there is memory error, you will see a memory error report. Note that macOS only supports address sanitizer without leak sanitizer.

In some cases, address sanitizer might affect the usability of the debugger. In this case, you might need to disable all sanitizers by configuring the CMake project with:

$ cmake -DCMAKE_BUILD_TYPE=Debug -DBUSTUB_SANITIZER= ..

Development Hints

You can use BUSTUB_ASSERT for assertions in debug mode. Note that the statements within BUSTUB_ASSERT will NOT be executed in release mode. If you have something to assert in all cases, use BUSTUB_ENSURE instead.

We will test your implementation in release mode. To compile your solution in release mode,

$ mkdir build_rel && cd build_rel
$ cmake -DCMAKE_BUILD_TYPE=Release ..

Post all of your questions about this project on Piazza. Do not email the TAs directly with questions.

TAs will not look into your code or help you debug in this project.

Grading Rubric

In order to pass this project, you must ensure your code follows the following guidelines:

  1. Does the submission successfully execute all of the test cases and produce the correct answer?
  2. Does the submission execute without any memory leaks?
  3. Does the submission follow the code formatting and style policies?

Note that we will use additional test cases to grade your submission that are more complex than the sample test cases that we provide you in future projects.

Late Policy

There are no late days for this project.

Submission

You will submit your implementation to Gradescope:

Run this command in build directory and it will create a zip archive called project0-submission.zip that you can submit to Gradescope.

$ make submit-p0

Although you are allowed submit your answers as many times as you like, you should not treat Gradescope as your only debugging tool. Most students submit their projects near the deadline, and thus Gradescope will take longer to process the requests. You may not get feedback in a timely manner to help you debug problems. Furthermore, the output from Gradescope is unlikely to be as informative as the output from a debugger (like gdb), provided you invest some time in learning to use it.

CMU students should use the Gradescope course code announced on Piazza.

Collaboration Policy

WARNING: All of the code for this project must be your own. You may not copy source code from other students or other sources that you find on the web. Plagiarism will not be tolerated. See CMU's Policy on Academic Integrity for additional information.