├── tests ├── test.cpp └── CMakeLists.txt ├── Dockerfile ├── LICENSE.txt ├── CMakeLists.txt ├── README.md └── gcc_build.sh /tests/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | TEST(basic, test_basic) 4 | { 5 | ASSERT_EQ(1, 1); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 2 | 3 | set(CODING_TESTS tests) 4 | file(GLOB TEST_SRC_FILES ${PROJECT_SOURCE_DIR}/tests/*.cpp) 5 | 6 | add_executable(${CODING_TESTS} ${TEST_SRC_FILES}) 7 | set_target_properties(${CODING_TESTS} PROPERTIES COMPILE_FLAGS "-Wl,--whole-archive ${OPTIMIZATION_OVERRIDE}") 8 | target_link_libraries(${CODING_TESTS} 9 | ${CMAKE_THREAD_LIBS_INIT} 10 | ${Boost_LIBRARIES} 11 | GTest::GTest GTest::Main 12 | ${COMMON_LIBS}) 13 | add_test(coding_tests ${CODING_TESTS}) 14 | install(TARGETS ${CODING_TESTS} RUNTIME DESTINATION ${TARGET_TEST_DIR}) 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:centos7 2 | 3 | ENV GCC_VERSION="10.1.0" 4 | ENV GCC_TAR="gcc-${GCC_VERSION}.tar.xz" 5 | 6 | COPY gcc_build.sh /tmp/gcc_build.sh 7 | RUN yum install -y bzip2 gcc make gcc-c++ glibc bison texinfo \ 8 | && /tmp/gcc_build.sh ${GCC_VERSION} \ 9 | && mkdir -p /opt/gcc \ 10 | && cd /opt/gcc \ 11 | && tar -Jxf /root/${GCC_TAR} \ 12 | && touch /var/lib/rpm/* \ 13 | && yum install -y https://centos7.iuscommunity.org/ius-release.rpm \ 14 | && yum install -y git vim cmake3 glibc-devel \ 15 | && rm /root/${GCC_TAR} /${GCC_TAR} \ 16 | && rm -rf /gcc-${GCC_VERSION} \ 17 | && yum remove -y gcc cpp \ 18 | && yum clean all && rm -rf /var/cache/yum/* \ 19 | && rm -rf /objdir 20 | 21 | ENV GCC_ROOT=/opt/gcc/gcc-${GCC_VERSION} 22 | ENV PATH=${GCC_ROOT}/bin${PATH:+:${PATH}} 23 | ENV MANPATH=${GCC_ROOT}/share/man:${MANPATH} 24 | ENV INFOPATH=${GCC_ROOT}/share/info${INFOPATH:+:${INFOPATH}} 25 | ENV LD_LIBRARY_PATH=${GCC_ROOT}/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} 26 | 27 | RUN yum install -y boost-devel gtest-devel && ln -s /usr/bin/cmake3 /usr/bin/cmake 28 | 29 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Aquatic Technologies LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | project(coding_test) 3 | 4 | if (POLICY CMP0015) 5 | cmake_policy(SET CMP0015 NEW) 6 | endif () 7 | 8 | enable_testing() 9 | 10 | find_package(Boost COMPONENTS REQUIRED date_time filesystem iostreams program_options system) 11 | find_package(GTest REQUIRED) 12 | 13 | include_directories(${GTEST_INCLUDE_DIRS}) 14 | include_directories("${PROJECT_SOURCE_DIR}/") 15 | 16 | SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 17 | 18 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++2a -g --concepts") 19 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wno-deprecated") 20 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") 21 | 22 | message(STATUS "===============================================================") 23 | if (NOT CMAKE_BUILD_TYPE) 24 | set(CMAKE_BUILD_TYPE "Debug") 25 | endif () 26 | 27 | message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") 28 | string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type) 29 | 30 | set(OPTIMIZATION_OVERRIDE_Release="-Os") 31 | set(OPTIMIZATION_OVERRIDE="OPTIMIZATION_OVERRIDE_${CMAKE_BUILD_TYPE}") 32 | 33 | message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") 34 | message(STATUS "CMAKE_CXX_FLAGS_${build_type}: ${CMAKE_CXX_FLAGS_${build_type}}") 35 | 36 | set(COMMON_LIBS rt dl stdc++fs) 37 | add_subdirectory(${PROJECT_SOURCE_DIR}/tests) 38 | 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aquatic C++ Coding Exercise 2 | 3 | ## Project 4 | 5 | We need to track a large number of items for insertion via a key. We want the performance to be good, but we also want to minimize the performance variance so as to have a more stable performance metric to plan on. Currently, we use a traditional hash map for the task, and we’ve found that it is both too high on latency and has too high of a variance, when profiled. An internal developer has mentioned the concept of an Open Addressing Hash-Table, and we’d like to implement one so that we might compare its performance characteristics. 6 | 7 | An open-addressing hash-table has an overly sized internal array, same as a traditional collision-list based hash-table, but instead of using the hash to find a collision list and then inserting the element in such a list, the item goes directly into the hashed location within the internal array. If an element is already there, we will place the new element in the first free location, by stepping incrementally from the hash location. As such, "collision lists" exist as consecutively filled elements in the internal array. 8 | 9 | Your task is to create an open-addressing hash-table with at least the following: 10 | 11 | * Default construction 12 | * Insertion of elements via a key 13 | * Retrieval of elements based on the key 14 | * Reporting current size of the container 15 | 16 | Notes: 17 | 18 | * Repeated insertions for the same key should overwrite each other. 19 | * The container should provide storage to some factor of what the machine will reasonably allow. (No predefined maximum number of items) 20 | * We do not expect you to fully imeplement STL compatibility in this task, and would instead ask you to approach this problem in the manner in which you would expect to do so if given such a task. 21 | * While templates are permissible, the intent of this exercise is to gauge your comfort in C++ programming while respecting your time. If template programming is not your forte, then feel free to just have the facility store ints, or strings, or whatever other basic type you feel appropriate, for now. 22 | * At this time, we will leave removal of items as a future extension 23 | * There is no explicit need for iterator support, though you are welcome to do so, if you feel it best for your implementation. 24 | 25 | ## Getting started 26 | 27 | First, make sure to pull this code base to your local system: 28 | 29 | ``` 30 | git clone https://github.com/aquanauts/cpp_interview.git 31 | cd cpp_interview 32 | ``` 33 | 34 | ## Provided 35 | 36 | Notice: While Aquatic deals with social distancing, we are performing virtual interviews. As such, the need for a standardized build environment (below) is largely mitigated by your interview being done primarily through screen share of your setup, rather than on an Aquatic machine at our office. As such, free to use the build environment provided, below. If that proves troublesome, such as trying to develop on a Mac or recent version of Fedora that requires podman, then the please feel free to develop in whatever environment you have available to you, so long as you will be able to share your screen in a virtual interview. 37 | 38 | Lacking any standard build environment, we provide a docker-based setup to get you going. The expectation is that the code provided would be able to build within this environment, but you are free to develop in whatever environment makes you comfortable. 39 | 40 | The environment provides: 41 | 42 | * centos 7 43 | * gcc 10.1.0, built from source and installed in `/opt/gcc` 44 | * gtest-devel installed via yum 45 | * boost-devel installed via yum 46 | * cmake3 (symbolycally linked as `/usr/bin/cmake` for convenience) 47 | * removes default compilers to avoid confusion 48 | 49 | To pull the pre-built docker image, please see: https://hub.docker.com/repository/docker/rwdougla/aq_cpp 50 | 51 | OR 52 | 53 | To build docker image from source 54 | 55 | `docker build . -t rwdougla/aq_cpp` 56 | 57 | Run docker: (Take care to personalize to where you pulled this repo) 58 | 59 | ``docker run --rm -it -v `pwd`:/workspace/ rwdougla/aq_cpp bash`` 60 | 61 | Inside Docker container: 62 | 63 | ``` 64 | cd /workspace/ 65 | mkdir debug && cd debug 66 | cmake .. 67 | make 68 | ./tests/tests 69 | ``` 70 | -------------------------------------------------------------------------------- /gcc_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | ROOT=$(pwd) 6 | SCRIPT_DIR=`dirname "$0"` 7 | VERSION=$1 8 | LANGUAGES=c,c++ 9 | if echo ${VERSION} | grep 'trunk'; then 10 | VERSION=trunk-$(date +%Y%m%d) 11 | URL=svn://gcc.gnu.org/svn/gcc/trunk 12 | MAJOR=9 13 | MAJOR_MINOR=9-trunk 14 | elif echo ${VERSION} | grep 'snapshot-'; then 15 | VERSION=${VERSION/#snapshot-/} 16 | TARBALL=gcc-${VERSION}.tar.xz 17 | URL=ftp://gcc.gnu.org/pub/gcc/snapshots/${VERSION}/${TARBALL} 18 | MAJOR=$(echo ${VERSION} | grep -oE '^[0-9]+') 19 | MAJOR_MINOR=${MAJOR}-snapshot 20 | else 21 | MAJOR=$(echo ${VERSION} | grep -oE '^[0-9]+') 22 | MAJOR_MINOR=$(echo ${VERSION} | grep -oE '^[0-9]+\.[0-9]+') 23 | TARBALL=gcc-${VERSION}.tar.bz2 24 | if [[ "${MAJOR}" -gt 7 ]]; then TARBALL=gcc-${VERSION}.tar.xz; fi 25 | if [[ "${MAJOR_MINOR}" = "7.2" ]]; then TARBALL=gcc-${VERSION}.tar.xz; fi 26 | if [[ "${MAJOR_MINOR}" = "7.3" ]]; then TARBALL=gcc-${VERSION}.tar.xz; fi 27 | if [[ "${MAJOR_MINOR}" = "7.4" ]]; then TARBALL=gcc-${VERSION}.tar.xz; fi 28 | if [[ "${MAJOR_MINOR}" = "5.5" ]]; then TARBALL=gcc-${VERSION}.tar.xz; fi 29 | if [[ "${MAJOR_MINOR}" = "6.4" ]]; then TARBALL=gcc-${VERSION}.tar.xz; fi 30 | URL=ftp://ftp.gnu.org/gnu/gcc/gcc-${VERSION}/${TARBALL} 31 | fi 32 | OUTPUT=${HOME}/gcc-${VERSION}.tar.xz 33 | S3OUTPUT="" 34 | if echo $2 | grep s3://; then 35 | S3OUTPUT=$2 36 | else 37 | OUTPUT=${2-${HOME}/gcc-${VERSION}.tar.xz} 38 | fi 39 | 40 | # Workaround for Ubuntu builds 41 | export LIBRARY_PATH=/usr/lib/x86_64-linux-gnu 42 | STAGING_DIR=$(pwd)/staging 43 | INSTALL_TARGET=install-strip 44 | rm -rf ${STAGING_DIR} 45 | mkdir -p ${STAGING_DIR} 46 | 47 | if echo ${URL} | grep svn://; then 48 | rm -rf gcc-${VERSION} 49 | svn checkout -q ${URL} gcc-${VERSION} 50 | elif echo ${URL} | grep .git; then 51 | rm -rf gcc-${VERSION} 52 | git clone -q ${URL} gcc-${VERSION} 53 | pushd gcc-${VERSION} 54 | git checkout ${BRANCH} 55 | popd 56 | else 57 | if [[ ! -e ${TARBALL} ]]; then 58 | echo "Fetching GCC" from ${URL}... 59 | curl -L -O ${URL} 60 | fi 61 | rm -rf gcc-${VERSION} 62 | echo "Extracting GCC..." 63 | tar axf ${TARBALL} 64 | fi 65 | 66 | echo "Downloading prerequisites" 67 | pushd gcc-${VERSION} 68 | if [[ -f ./contrib/download_prerequisites ]]; then 69 | ./contrib/download_prerequisites 70 | else 71 | # Older GCCs lacked it, so this is one stolen from GCC 4.6.1 72 | ../download_prerequisites 73 | fi 74 | popd 75 | 76 | applyPatchesAndConfig() { 77 | local PATCH_DIR=${ROOT}/patches/$1 78 | local PATCH="" 79 | if [[ -d ${PATCH_DIR} ]]; then 80 | echo "Applying patches from ${PATCH_DIR}" 81 | pushd gcc-${VERSION} 82 | for PATCH in ${PATCH_DIR}/*; do 83 | patch -p1 < ${PATCH} 84 | done 85 | popd 86 | fi 87 | 88 | local CONFIG_DIR=${ROOT}/config/$1 89 | local CONFIG_FILE="" 90 | if [[ -d ${CONFIG_DIR} ]]; then 91 | echo "Applying config from ${CONFIG_DIR}" 92 | for CONFIG_FILE in ${CONFIG_DIR}/*; do 93 | . ${CONFIG_FILE} 94 | done 95 | fi 96 | } 97 | 98 | CONFIG="" 99 | CONFIG+=" --build=x86_64-linux-gnu" 100 | CONFIG+=" --host=x86_64-linux-gnu" 101 | CONFIG+=" --target=x86_64-linux-gnu" 102 | CONFIG+=" --disable-bootstrap" 103 | CONFIG+=" --enable-multiarch" 104 | CONFIG+=" --with-abi=m64" 105 | CONFIG+=" --with-multilib-list=m64" 106 | CONFIG+=" --enable-multilib" 107 | CONFIG+=" --enable-clocale=gnu" 108 | CONFIG+=" --enable-languages=${LANGUAGES}" 109 | CONFIG+=" --enable-ld=yes" 110 | CONFIG+=" --enable-gold=yes" 111 | CONFIG+=" --enable-libstdcxx-debug" 112 | CONFIG+=" --enable-libstdcxx-time=yes" 113 | CONFIG+=" --enable-linker-build-id" 114 | CONFIG+=" --enable-lto" 115 | CONFIG+=" --enable-plugins" 116 | CONFIG+=" --enable-threads=posix" 117 | CONFIG+=" --with-pkgversion=Aquatic-Internal-Build" 118 | 119 | applyPatchesAndConfig gcc${MAJOR} 120 | applyPatchesAndConfig gcc${MAJOR_MINOR} 121 | applyPatchesAndConfig gcc${VERSION} 122 | 123 | echo "Will configure with ${CONFIG}" 124 | 125 | if [[ -z "${BINUTILS_VERSION}" ]]; then 126 | echo "Using host binutils $(ld -v)" 127 | else 128 | source ${ROOT}/overrides.sh ${MAJOR} 129 | echo "Fetching binutils ${BINUTILS_VERSION}" 130 | if [[ ! -e binutils-${BINUTILS_VERSION}.tar.bz2 ]]; then 131 | curl -L -O http://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VERSION}.tar.bz2 132 | fi 133 | BINUTILS_DIR=binutils-${BINUTILS_VERSION} 134 | rm -rf ${BINUTILS_DIR} 135 | tar jxf binutils-${BINUTILS_VERSION}.tar.bz2 136 | mkdir ${BINUTILS_DIR}/objdir 137 | pushd ${BINUTILS_DIR}/objdir 138 | ../configure --prefix=${STAGING_DIR} ${CONFIG} 139 | make -j$(nproc) 140 | make ${INSTALL_TARGET} 141 | popd 142 | fi 143 | 144 | mkdir -p objdir 145 | pushd objdir 146 | ../gcc-${VERSION}/configure --prefix=${STAGING_DIR} ${CONFIG} 147 | make -j$(nproc) 148 | make ${INSTALL_TARGET} 149 | popd 150 | 151 | export XZ_DEFAULTS="-T 0" 152 | tar Jcf ${OUTPUT} --transform "s,^./,./gcc-${VERSION}/," -C ${STAGING_DIR} . 153 | 154 | if [[ ! -z "${S3OUTPUT}" ]]; then 155 | s3cmd put --rr ${OUTPUT} ${S3OUTPUT} 156 | fi 157 | 158 | --------------------------------------------------------------------------------