├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── common ├── CMakeLists.txt ├── arrayofitems.cpp ├── arrayofitems.h ├── collectiontest1.cpp ├── collectiontest1.h ├── collectiontest2.cpp ├── collectiontest2.h ├── experiment.cpp ├── experiment.h ├── hashtable1.cpp └── hashtable1.h └── projects ├── arrayofitems ├── CMakeLists.txt └── main.cpp └── hashtable1 ├── CMakeLists.txt └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *~ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mintomic"] 2 | path = mintomic 3 | url = https://github.com/mintomic/mintomic.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Jeff Preshing 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | (1) Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | (2) Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | (3)The name of the author may not be used to 17 | endorse or promote products derived from this software without 18 | specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 24 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *** **Update 2015-02-06: Mintomic is no longer being maintained.** Native C atomics are available in [Turf](https://github.com/preshing/turf) instead: https://github.com/preshing/turf/tree/master/turf/c *** 2 | 3 | --- 4 | 5 | This repository contains some data structures and sample applications which demonstrate low-level lock-free concepts using [Mintomic](http://mintomic.github.io/). There are two sample applications: 6 | 7 | * ArrayOfItems 8 | * HashTable1 9 | 10 | [CMake](http://www.cmake.org/cmake/resources/software.html) is required. 11 | 12 | ## Build Instructions 13 | 14 | The following steps work in Windows, Linux and MacOS. 15 | 16 | First, clone this repository as you normally would. 17 | 18 | $ git clone https://github.com/mintomic/samples 19 | 20 | The repository contains Mintomic as a submodule. If you cloned using the above command line, you'll need to manually update the submodule. Otherwise, the `mintomic/` subdirectory will be empty, and things won't build. 21 | 22 | $ cd samples 23 | $ git submodule update --init 24 | 25 | Next, suppose you want to build the ArrayOfItems sample. Descend into the `projects/arrayofitems/` subdirectory and run CMake as you normally would. It's customary to run it from a subdirectory named `build/`. 26 | 27 | $ cd projects/arrayofitems 28 | $ mkdir build 29 | $ cd build 30 | $ cmake .. 31 | 32 | If the above CMake command line does not generate the project type you want, try again using CMake's `-G` option, or use the CMake GUI. 33 | 34 | To generate an Xcode project for iOS devices, use the following CMake command line: 35 | 36 | $ cmake -DCMAKE_TOOLCHAIN_FILE=../../../mintomic/cmake/iOS.cmake -G "Xcode" .. 37 | 38 | After running CMake, you will be left with some project/solution files for your IDE, or some Unix Makefiles. You should know what to do at this point. Remember to select ArrayOfItems as the Startup Project (Visual Studio) or the Active Scheme (Xcode). 39 | 40 | It's highly recommended to build and run the Release configuration. Select this in your IDE the usual way, or if you're working with Unix Makefiles, specify the build type on the CMake command line: 41 | 42 | $ cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" .. 43 | -------------------------------------------------------------------------------- /common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Common) 2 | 3 | include_directories(.) 4 | include_directories(../mintomic/include) 5 | 6 | file(GLOB INCFILES *.h) 7 | file(GLOB SRCFILES *.c *.cpp) 8 | add_library(Common ${SRCFILES} ${INCFILES}) 9 | include(../mintomic/cmake/BuildSettings.cmake) 10 | -------------------------------------------------------------------------------- /common/arrayofitems.cpp: -------------------------------------------------------------------------------- 1 | #include "arrayofitems.h" 2 | #include 3 | #include 4 | 5 | 6 | #define USE_FAST_SETITEM 1 7 | 8 | 9 | //---------------------------------------------- 10 | ArrayOfItems::ArrayOfItems(uint32_t arraySize) 11 | { 12 | // Initialize cells 13 | m_arraySize = arraySize; 14 | m_entries = new Entry[arraySize]; 15 | Clear(); 16 | } 17 | 18 | 19 | //---------------------------------------------- 20 | ArrayOfItems::~ArrayOfItems() 21 | { 22 | // Delete cells 23 | delete[] m_entries; 24 | } 25 | 26 | 27 | //---------------------------------------------- 28 | #if USE_FAST_SETITEM 29 | void ArrayOfItems::SetItem(uint32_t key, uint32_t value) 30 | { 31 | assert(key != 0); 32 | assert(value != 0); 33 | 34 | for (uint32_t idx = 0;; idx++) 35 | { 36 | // Load the key that was there. 37 | uint32_t probedKey = mint_load_32_relaxed(&m_entries[idx].key); 38 | if (probedKey != key) 39 | { 40 | // The entry was either free, or contains another key. 41 | if (probedKey != 0) 42 | continue; // Usually, it contains another key. Keep probing. 43 | 44 | // The entry was free. Now let's try to take it using a CAS. 45 | uint32_t prevKey = mint_compare_exchange_strong_32_relaxed(&m_entries[idx].key, 0, key); 46 | if ((prevKey != 0) && (prevKey != key)) 47 | continue; // Another thread just stole it from underneath us. 48 | 49 | // Either we just added the key, or another thread did. 50 | } 51 | 52 | // Store the value in this array entry. 53 | mint_store_32_relaxed(&m_entries[idx].value, value); 54 | return; 55 | } 56 | } 57 | #else 58 | void ArrayOfItems::SetItem(uint32_t key, uint32_t value) 59 | { 60 | assert(key != 0); 61 | assert(value != 0); 62 | 63 | for (uint32_t idx = 0;; idx++) 64 | { 65 | uint32_t prevKey = mint_compare_exchange_strong_32_relaxed(&m_entries[idx].key, 0, key); 66 | if ((prevKey == 0) || (prevKey == key)) 67 | { 68 | mint_store_32_relaxed(&m_entries[idx].value, value); 69 | return; 70 | } 71 | } 72 | } 73 | #endif 74 | 75 | //---------------------------------------------- 76 | uint32_t ArrayOfItems::GetItem(uint32_t key) 77 | { 78 | assert(key != 0); 79 | 80 | for (uint32_t idx = 0;; idx++) 81 | { 82 | uint32_t probedKey = mint_load_32_relaxed(&m_entries[idx].key); 83 | if (probedKey == key) 84 | return mint_load_32_relaxed(&m_entries[idx].value); 85 | if (probedKey == 0) 86 | return 0; 87 | } 88 | } 89 | 90 | 91 | //---------------------------------------------- 92 | uint32_t ArrayOfItems::GetItemCount() 93 | { 94 | for (uint32_t idx = 0;; idx++) 95 | { 96 | if ((mint_load_32_relaxed(&m_entries[idx].key) == 0) 97 | || (mint_load_32_relaxed(&m_entries[idx].value) == 0)) 98 | return idx; 99 | } 100 | } 101 | 102 | 103 | //---------------------------------------------- 104 | void ArrayOfItems::Clear() 105 | { 106 | memset(m_entries, 0, sizeof(Entry) * m_arraySize); 107 | } 108 | -------------------------------------------------------------------------------- /common/arrayofitems.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_ARRAYOFITEMS_H__ 2 | #define __COMMON_ARRAYOFITEMS_H__ 3 | 4 | #include 5 | 6 | 7 | //---------------------------------------------- 8 | // ArrayOfItems 9 | // 10 | // Maps 32-bit integers to 32-bit integers. 11 | // You can call SetItem and GetItem from several threads simultaneously. 12 | // Both operations are implemented using linear search. 13 | // SetItem is lock-free. 14 | // GetItem is wait-free. 15 | // You can't pass 0 as a key or a value to SetItem. 16 | // In the m_entries array, key = 0 is reserved to indicate an unused entry. 17 | // The array must be preallocated to a large enough size ahead of time. 18 | // You can Clear the array, but only at a time when there are no other calls being made from other threads. 19 | //---------------------------------------------- 20 | class ArrayOfItems 21 | { 22 | public: 23 | struct Entry 24 | { 25 | mint_atomic32_t key; 26 | mint_atomic32_t value; 27 | }; 28 | 29 | private: 30 | Entry* m_entries; 31 | uint32_t m_arraySize; 32 | 33 | public: 34 | ArrayOfItems(uint32_t arraySize); 35 | ~ArrayOfItems(); 36 | 37 | // Basic operations 38 | void SetItem(uint32_t key, uint32_t value); 39 | uint32_t GetItem(uint32_t key); 40 | uint32_t GetItemCount(); 41 | void Clear(); 42 | }; 43 | 44 | 45 | #endif // __COMMON_ARRAYOFITEMS_H__ 46 | -------------------------------------------------------------------------------- /common/collectiontest1.cpp: -------------------------------------------------------------------------------- 1 | #include "collectiontest1.h" 2 | #include "experiment.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | //-------------------------------------------------------------- 14 | // In this experiment, each thread adds a different list of items to the 15 | // collection. No two threads add the same key. 16 | //-------------------------------------------------------------- 17 | namespace CollectionTest1 18 | { 19 | struct Result 20 | { 21 | TrialParams params; 22 | BasePlugin* plugin; 23 | mint_atomic32_t failed; 24 | }; 25 | 26 | void threadFunc(int threadNum, void* param) 27 | { 28 | Result* result = (Result*) param; 29 | 30 | LWLOG("Begin loop", threadNum); 31 | if (!result->plugin->DoThread(threadNum, &result->params)) 32 | mint_store_32_relaxed(&result->failed, 1); 33 | LWLOG("End loop", threadNum); 34 | } 35 | 36 | class Experiment : public IExperiment 37 | { 38 | private: 39 | BasePlugin* m_plugin; 40 | uint32_t m_numPairs; 41 | uint32_t m_tableSize; 42 | int m_numThreads; 43 | 44 | public: 45 | Experiment(BasePlugin* plugin, uint32_t numPairs, uint32_t tableSize, int numThreads) 46 | : m_plugin(plugin) 47 | , m_numPairs(numPairs) 48 | , m_tableSize(tableSize) 49 | , m_numThreads(numThreads) 50 | { 51 | } 52 | 53 | void Run(TimeKeeper& timeKeeper) 54 | { 55 | // Warm up some threads 56 | ThreadSynchronizer threads(m_numThreads); 57 | 58 | // Allocate storage 59 | Result result; 60 | result.params.pairs = new Pair[m_numPairs]; 61 | result.params.numPairs = m_numPairs; 62 | result.params.numThreads = m_numThreads; 63 | result.plugin = m_plugin; 64 | result.failed._nonatomic = 0; 65 | 66 | // Fill in pairs 67 | Random keySeq; 68 | Random valueSeq; 69 | for (uint32_t i = 0; i < m_numPairs; i++) 70 | { 71 | do { result.params.pairs[i].key = keySeq.generateUnique32(); } while (result.params.pairs[i].key == 0); 72 | do { result.params.pairs[i].value = valueSeq.generateUnique32(); } while (result.params.pairs[i].value == 0); 73 | } 74 | 75 | // Kick threads through the experiment several times 76 | for (int i = 0; i < 10; i++) 77 | { 78 | m_plugin->CreateCollection(m_tableSize); 79 | threads.run(threadFunc, &result.params); 80 | 81 | // Check result 82 | if (result.failed._nonatomic || !m_plugin->CheckCollection(&result.params)) 83 | timeKeeper.failures++; 84 | 85 | // Return average thread time 86 | timeKeeper.totalTime += threads.getAverageThreadRunningTime(); 87 | timeKeeper.trials++; 88 | } 89 | } 90 | }; 91 | 92 | IExperiment* CreateExperiment(BasePlugin* plugin, uint32_t numItems, uint32_t tableSize) 93 | { 94 | return new Experiment(plugin, numItems, tableSize, 2); 95 | } 96 | } // namespace CollectionTest1 97 | -------------------------------------------------------------------------------- /common/collectiontest1.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_COLLECTIONTEST1_H__ 2 | #define __COMMON_COLLECTIONTEST1_H__ 3 | 4 | #include 5 | 6 | class IExperiment; 7 | 8 | #define TEST_LOOKUP_1 1 9 | 10 | 11 | //-------------------------------------------------------------- 12 | // In this experiment, each thread adds a different list of items to the 13 | // collection. No two threads add the same key. 14 | //-------------------------------------------------------------- 15 | namespace CollectionTest1 16 | { 17 | struct Pair 18 | { 19 | uint32_t key; 20 | uint32_t value; 21 | }; 22 | 23 | struct TrialParams 24 | { 25 | Pair* pairs; 26 | uint32_t numPairs; 27 | int numThreads; 28 | }; 29 | 30 | class BasePlugin 31 | { 32 | public: 33 | virtual ~BasePlugin() {} 34 | virtual void CreateCollection(uint32_t size) = 0; 35 | virtual bool DoThread(int threadNum, TrialParams* params) = 0; 36 | virtual bool CheckCollection(TrialParams* params) = 0; 37 | }; 38 | 39 | //-------------------------------------------------------------- 40 | // Instantiate this class with the type of collection you want to test (eg. ArrayOfItems). 41 | //-------------------------------------------------------------- 42 | template class Plugin : public BasePlugin 43 | { 44 | private: 45 | T* m_collection; 46 | 47 | public: 48 | Plugin() : m_collection(NULL) 49 | { 50 | } 51 | 52 | ~Plugin() 53 | { 54 | if (m_collection) 55 | delete m_collection; 56 | } 57 | 58 | virtual void CreateCollection(uint32_t size) 59 | { 60 | if (m_collection) 61 | delete m_collection; 62 | m_collection = new T(size); 63 | } 64 | 65 | virtual bool DoThread(int threadNum, TrialParams* params) 66 | { 67 | uint32_t lo = params->numPairs * threadNum / params->numThreads; 68 | uint32_t hi = params->numPairs * (threadNum + 1) / params->numThreads; 69 | 70 | for (uint32_t i = lo; i < hi; i++) 71 | { 72 | Pair* pair = ¶ms->pairs[i]; 73 | m_collection->SetItem(pair->key, pair->value); 74 | } 75 | 76 | #if TEST_LOOKUP_1 77 | for (uint32_t i = lo; i < hi; i++) 78 | { 79 | Pair* pair = ¶ms->pairs[i]; 80 | uint32_t value = m_collection->GetItem(pair->key); 81 | if (value != pair->value) 82 | return false; 83 | } 84 | #endif 85 | 86 | return true; 87 | } 88 | 89 | virtual bool CheckCollection(TrialParams* params) 90 | { 91 | for (uint32_t i = 0; i < params->numPairs; i++) 92 | { 93 | Pair* pair = ¶ms->pairs[i]; 94 | if (m_collection->GetItem(pair->key) != pair->value) 95 | return false; 96 | } 97 | return true; 98 | } 99 | }; 100 | 101 | //-------------------------------------------------------------- 102 | // Return an IExperiment object suitable for passing to LoopForever. 103 | //-------------------------------------------------------------- 104 | IExperiment* CreateExperiment(BasePlugin* plugin, uint32_t numItems, uint32_t tableSize); 105 | } // namespace CollectionTest1 106 | 107 | 108 | #endif // __COMMON_COLLECTIONTEST1_H__ 109 | -------------------------------------------------------------------------------- /common/collectiontest2.cpp: -------------------------------------------------------------------------------- 1 | #include "collectiontest2.h" 2 | #include "experiment.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | namespace CollectionTest2 14 | { 15 | struct Result 16 | { 17 | TrialParams params; 18 | BasePlugin* plugin; 19 | mint_atomic32_t failed; 20 | }; 21 | 22 | void threadFunc(int threadNum, void* param) 23 | { 24 | Result* result = (Result*) param; 25 | 26 | LWLOG("Begin loop", threadNum); 27 | if (!result->plugin->DoThread(threadNum, &result->params)) 28 | mint_store_32_relaxed(&result->failed, 1); 29 | LWLOG("End loop", threadNum); 30 | } 31 | 32 | class Experiment : public IExperiment 33 | { 34 | private: 35 | BasePlugin* m_plugin; 36 | uint32_t m_numItems; 37 | uint32_t m_tableSize; 38 | int m_numThreads; 39 | 40 | public: 41 | Experiment(BasePlugin* plugin, uint32_t numItems, uint32_t tableSize, int numThreads) 42 | : m_plugin(plugin) 43 | , m_numItems(numItems) 44 | , m_tableSize(tableSize) 45 | , m_numThreads(numThreads) 46 | { 47 | } 48 | 49 | void Run(TimeKeeper& timeKeeper) 50 | { 51 | // Warm up some threads 52 | ThreadSynchronizer threads(m_numThreads); 53 | 54 | // Allocate storage 55 | Result result; 56 | result.params.items = new ItemSet[m_numItems]; 57 | memset(result.params.items, 0, sizeof(ItemSet) * m_numItems); 58 | result.params.numItems = m_numItems; 59 | result.params.numThreads = m_numThreads; 60 | result.plugin = m_plugin; 61 | result.failed._nonatomic = 0; 62 | 63 | // Fill in pairs 64 | Random keySeq; 65 | Random valueRandom; 66 | for (uint32_t i = 0; i < m_numItems; i++) 67 | { 68 | do { result.params.items[i].key = keySeq.generateUnique32(); } while (result.params.items[i].key == 0); 69 | for (int j = 0; j < m_numThreads; j++) 70 | do { result.params.items[i].value[j] = valueRandom.generate32(); } while (result.params.items[i].value[j] == 0); 71 | } 72 | 73 | // Kick threads through the experiment several times 74 | for (int i = 0; i < 10; i++) 75 | { 76 | m_plugin->CreateCollection(m_tableSize); 77 | threads.run(threadFunc, &result.params); 78 | 79 | // Check result 80 | if (result.failed._nonatomic || !m_plugin->CheckCollection(&result.params)) 81 | timeKeeper.failures++; 82 | 83 | // Return average thread time 84 | timeKeeper.totalTime += threads.getAverageThreadRunningTime(); 85 | timeKeeper.trials++; 86 | } 87 | } 88 | }; 89 | 90 | IExperiment* CreateExperiment(BasePlugin* plugin, uint32_t numItems, uint32_t tableSize) 91 | { 92 | return new Experiment(plugin, numItems, tableSize, 2); 93 | } 94 | } // namespace CollectionTest2 95 | -------------------------------------------------------------------------------- /common/collectiontest2.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_COLLECTIONTEST2_H__ 2 | #define __COMMON_COLLECTIONTEST2_H__ 3 | 4 | #include 5 | 6 | class IExperiment; 7 | 8 | #define TEST_LOOKUP_2 1 9 | 10 | 11 | //-------------------------------------------------------------- 12 | // In this experiment, each thread adds the same set of keys to the 13 | // collection, but with different values. 14 | //-------------------------------------------------------------- 15 | namespace CollectionTest2 16 | { 17 | struct ItemSet 18 | { 19 | uint32_t key; 20 | uint32_t value[4]; 21 | }; 22 | 23 | struct TrialParams 24 | { 25 | ItemSet* items; 26 | uint32_t numItems; 27 | int numThreads; 28 | }; 29 | 30 | class BasePlugin 31 | { 32 | public: 33 | virtual ~BasePlugin() {} 34 | virtual void CreateCollection(uint32_t size) = 0; 35 | virtual bool DoThread(int arg, TrialParams* params) = 0; 36 | virtual bool CheckCollection(TrialParams* params) = 0; 37 | }; 38 | 39 | //-------------------------------------------------------------- 40 | // Instantiate this class with the type of collection you want to test (eg. ArrayOfItems). 41 | //-------------------------------------------------------------- 42 | template class Plugin : public BasePlugin 43 | { 44 | private: 45 | T* m_collection; 46 | 47 | public: 48 | Plugin() : m_collection(NULL) 49 | { 50 | } 51 | 52 | ~Plugin() 53 | { 54 | if (m_collection) 55 | delete m_collection; 56 | } 57 | 58 | virtual void CreateCollection(uint32_t size) 59 | { 60 | if (m_collection) 61 | delete m_collection; 62 | m_collection = new T(size); 63 | } 64 | 65 | virtual bool DoThread(int threadNum, TrialParams* params) 66 | { 67 | for (uint32_t i = 0; i < params->numItems; i++) 68 | { 69 | ItemSet* item = ¶ms->items[i]; 70 | m_collection->SetItem(item->key, item->value[threadNum]); 71 | } 72 | 73 | #if TEST_LOOKUP_2 74 | return CheckCollection(params); 75 | #endif 76 | 77 | return true; 78 | } 79 | 80 | virtual bool CheckCollection(TrialParams* params) 81 | { 82 | for (uint32_t i = 0; i < params->numItems; i++) 83 | { 84 | ItemSet* item = ¶ms->items[i]; 85 | uint32_t value = m_collection->GetItem(item->key); 86 | int j; 87 | for (j = 0; j < params->numThreads; j++) 88 | { 89 | if (value == item->value[j]) 90 | break; 91 | } 92 | if (j >= params->numThreads) 93 | return false; 94 | } 95 | if (m_collection->GetItemCount() != params->numItems) 96 | return false; 97 | return true; 98 | } 99 | }; 100 | 101 | //-------------------------------------------------------------- 102 | // Return an IExperiment object suitable for passing to LoopForever. 103 | //-------------------------------------------------------------- 104 | IExperiment* CreateExperiment(BasePlugin* plugin, uint32_t numItems, uint32_t tableSize); 105 | } // namespace CollectionTest2 106 | 107 | 108 | #endif // __COMMON_COLLECTIONTEST2_H__ 109 | -------------------------------------------------------------------------------- /common/experiment.cpp: -------------------------------------------------------------------------------- 1 | #include "experiment.h" 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | //-------------------------------------------------------------- 8 | // Run a set of experiments indefinitely while logging stats 9 | //-------------------------------------------------------------- 10 | void LoopForever(const std::vector& exps) 11 | { 12 | TimeKeeper* timeKeepers = new TimeKeeper[exps.size()]; 13 | 14 | mint_timer_tick_t logInterval = (mint_timer_tick_t) (1 * mint_timer_secondsToTicks); 15 | mint_timer_tick_t nextLog = mint_timer_get() + logInterval; 16 | for (;;) 17 | { 18 | for (size_t i = 0; i < exps.size(); i++) 19 | { 20 | exps[i]->Run(timeKeepers[i]); 21 | } 22 | 23 | mint_timer_tick_t now = mint_timer_get(); 24 | if (mint_timer_greater_or_equal(now, nextLog)) 25 | { 26 | printf("------------------------------------\n"); 27 | for (size_t j = 0; j < exps.size(); j++) 28 | printf("experiment #%d: %d trials, %d failures, average=%f ms\n", (int) j, timeKeepers[j].trials, timeKeepers[j].failures, timeKeepers[j].getAverageTime() * 1000); 29 | nextLog = now + logInterval; 30 | } 31 | } 32 | 33 | delete[] timeKeepers; 34 | } 35 | -------------------------------------------------------------------------------- /common/experiment.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_EXPERIMENT_H__ 2 | #define __COMMON_EXPERIMENT_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | //-------------------------------------------------------------- 10 | // Maintain a total of running times, trials and failures 11 | //-------------------------------------------------------------- 12 | struct TimeKeeper 13 | { 14 | mint_timer_tick_t totalTime; 15 | uint32_t trials; 16 | uint32_t failures; 17 | 18 | TimeKeeper() 19 | { 20 | totalTime = 0; 21 | trials = 0; 22 | failures = 0; 23 | } 24 | double getAverageTime() const 25 | { 26 | if (trials == 0) 27 | return 0; 28 | return totalTime * mint_timer_ticksToSeconds / trials; 29 | } 30 | }; 31 | 32 | //-------------------------------------------------------------- 33 | // Base interface for any experiment that runs ad infinitum 34 | //-------------------------------------------------------------- 35 | class IExperiment 36 | { 37 | public: 38 | virtual ~IExperiment() {}; 39 | virtual void Run(TimeKeeper& timeKeeper) = 0; 40 | }; 41 | 42 | //-------------------------------------------------------------- 43 | // Run a set of experiments indefinitely while logging stats 44 | //-------------------------------------------------------------- 45 | void LoopForever(const std::vector& exps); 46 | 47 | 48 | #endif // __COMMON_EXPERIMENT_H__ 49 | -------------------------------------------------------------------------------- /common/hashtable1.cpp: -------------------------------------------------------------------------------- 1 | #include "hashtable1.h" 2 | #include 3 | #include 4 | 5 | 6 | #define USE_FAST_SETITEM 1 7 | 8 | 9 | //---------------------------------------------- 10 | // from code.google.com/p/smhasher/wiki/MurmurHash3 11 | inline static uint32_t integerHash(uint32_t h) 12 | { 13 | h ^= h >> 16; 14 | h *= 0x85ebca6b; 15 | h ^= h >> 13; 16 | h *= 0xc2b2ae35; 17 | h ^= h >> 16; 18 | return h; 19 | } 20 | 21 | 22 | //---------------------------------------------- 23 | HashTable1::HashTable1(uint32_t arraySize) 24 | { 25 | // Initialize cells 26 | assert((arraySize & (arraySize - 1)) == 0); // Must be a power of 2 27 | m_arraySize = arraySize; 28 | m_entries = new Entry[arraySize]; 29 | Clear(); 30 | } 31 | 32 | 33 | //---------------------------------------------- 34 | HashTable1::~HashTable1() 35 | { 36 | // Delete cells 37 | delete[] m_entries; 38 | } 39 | 40 | 41 | //---------------------------------------------- 42 | #if USE_FAST_SETITEM 43 | void HashTable1::SetItem(uint32_t key, uint32_t value) 44 | { 45 | assert(key != 0); 46 | assert(value != 0); 47 | 48 | for (uint32_t idx = integerHash(key);; idx++) 49 | { 50 | idx &= m_arraySize - 1; 51 | 52 | // Load the key that was there. 53 | uint32_t probedKey = mint_load_32_relaxed(&m_entries[idx].key); 54 | if (probedKey != key) 55 | { 56 | // The entry was either free, or contains another key. 57 | if (probedKey != 0) 58 | continue; // Usually, it contains another key. Keep probing. 59 | 60 | // The entry was free. Now let's try to take it using a CAS. 61 | uint32_t prevKey = mint_compare_exchange_strong_32_relaxed(&m_entries[idx].key, 0, key); 62 | if ((prevKey != 0) && (prevKey != key)) 63 | continue; // Another thread just stole it from underneath us. 64 | 65 | // Either we just added the key, or another thread did. 66 | } 67 | 68 | // Store the value in this array entry. 69 | mint_store_32_relaxed(&m_entries[idx].value, value); 70 | return; 71 | } 72 | } 73 | #else 74 | void HashTable1::SetItem(uint32_t key, uint32_t value) 75 | { 76 | assert(key != 0); 77 | assert(value != 0); 78 | 79 | for (uint32_t idx = integerHash(key);; idx++) 80 | { 81 | idx &= m_arraySize - 1; 82 | 83 | uint32_t prevKey = mint_compare_exchange_strong_32_relaxed(&m_entries[idx].key, 0, key); 84 | if ((prevKey == 0) || (prevKey == key)) 85 | { 86 | mint_store_32_relaxed(&m_entries[idx].value, value); 87 | return; 88 | } 89 | } 90 | } 91 | #endif 92 | 93 | //---------------------------------------------- 94 | uint32_t HashTable1::GetItem(uint32_t key) 95 | { 96 | assert(key != 0); 97 | 98 | for (uint32_t idx = integerHash(key);; idx++) 99 | { 100 | idx &= m_arraySize - 1; 101 | 102 | uint32_t probedKey = mint_load_32_relaxed(&m_entries[idx].key); 103 | if (probedKey == key) 104 | return mint_load_32_relaxed(&m_entries[idx].value); 105 | if (probedKey == 0) 106 | return 0; 107 | } 108 | } 109 | 110 | 111 | //---------------------------------------------- 112 | uint32_t HashTable1::GetItemCount() 113 | { 114 | uint32_t itemCount = 0; 115 | for (uint32_t idx = 0; idx < m_arraySize; idx++) 116 | { 117 | if ((mint_load_32_relaxed(&m_entries[idx].key) != 0) 118 | && (mint_load_32_relaxed(&m_entries[idx].value) != 0)) 119 | itemCount++; 120 | } 121 | return itemCount; 122 | } 123 | 124 | 125 | //---------------------------------------------- 126 | void HashTable1::Clear() 127 | { 128 | memset(m_entries, 0, sizeof(Entry) * m_arraySize); 129 | } 130 | -------------------------------------------------------------------------------- /common/hashtable1.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_HASHTABLE1_H__ 2 | #define __COMMON_HASHTABLE1_H__ 3 | 4 | #include 5 | 6 | 7 | //---------------------------------------------- 8 | // The World's Simplest Lock-Free HashTable 9 | // 10 | // Maps 32-bit integers to 32-bit integers. 11 | // Uses open addressing with linear probing. 12 | // You can call SetItem and GetItem from several threads simultaneously. 13 | // GetItem is wait-free. 14 | // SetItem is lock-free. 15 | // You can't assign any value to key = 0. 16 | // In the m_cells array, key = 0 is reserved to indicate an unused cell. 17 | // You can't assign value = 0 to any key. 18 | // value = 0 means the key is unused. 19 | // The hash table never grows in size. 20 | // You can't delete individual items from the hash table. 21 | // You can Clear the hash table, but only at a time when there are no other calls being made from other threads. 22 | //---------------------------------------------- 23 | class HashTable1 24 | { 25 | public: 26 | struct Entry 27 | { 28 | mint_atomic32_t key; 29 | mint_atomic32_t value; 30 | }; 31 | 32 | private: 33 | Entry* m_entries; 34 | uint32_t m_arraySize; 35 | 36 | public: 37 | HashTable1(uint32_t arraySize); 38 | ~HashTable1(); 39 | 40 | // Basic operations 41 | void SetItem(uint32_t key, uint32_t value); 42 | uint32_t GetItem(uint32_t key); 43 | uint32_t GetItemCount(); 44 | void Clear(); 45 | }; 46 | 47 | 48 | #endif // __COMMON_HASHTABLE1_H__ 49 | -------------------------------------------------------------------------------- /projects/arrayofitems/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.5) 2 | set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE INTERNAL "" FORCE) 3 | project(ArrayOfItems) 4 | 5 | 6 | #------------------------------------------ 7 | # Configure the executable and all its dependencies 8 | #------------------------------------------ 9 | set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.mycompany.\${PRODUCT_NAME:identifier}") 10 | file(GLOB all_sources *) 11 | add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${all_sources}) 12 | include(../../mintomic/cmake/BuildSettings.cmake) 13 | 14 | # Libraries 15 | add_subdirectory(../../mintomic/src/mintomic mintomic) 16 | add_subdirectory(../../mintomic/src/mintsystem mintsystem) 17 | add_subdirectory(../../mintomic/src/mintpack mintpack) 18 | include_directories(../../mintomic/include) 19 | add_subdirectory(../../common common) 20 | include_directories(../..) 21 | 22 | # Add libraries with dependencies after dependents to satisfy ld linker. 23 | target_link_libraries(${PROJECT_NAME} Common) 24 | target_link_libraries(${PROJECT_NAME} MintPack) 25 | target_link_libraries(${PROJECT_NAME} MintSystem) 26 | get_target_property(TARGET_TYPE Mintomic TYPE) 27 | if(${TARGET_TYPE} STREQUAL "UTILITY") 28 | # Nothing to link with in the Mintomic subdirectory 29 | else() 30 | target_link_libraries(${PROJECT_NAME} Mintomic) 31 | endif() 32 | -------------------------------------------------------------------------------- /projects/arrayofitems/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | int main() 10 | { 11 | mint_timer_initialize(); 12 | 13 | std::vector exps; 14 | CollectionTest1::Plugin handler; 15 | exps.push_back(CollectionTest1::CreateExperiment(&handler, 4000, 4096)); 16 | CollectionTest2::Plugin handler2; 17 | exps.push_back(CollectionTest2::CreateExperiment(&handler2, 4000, 4096)); 18 | 19 | LoopForever(exps); 20 | 21 | for (size_t i = 0; i < exps.size(); i++) 22 | delete exps[i]; 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /projects/hashtable1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.5) 2 | set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE INTERNAL "" FORCE) 3 | project(HashTable1) 4 | 5 | 6 | #------------------------------------------ 7 | # Configure the executable and all its dependencies 8 | #------------------------------------------ 9 | set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.mycompany.\${PRODUCT_NAME:identifier}") 10 | file(GLOB all_sources *) 11 | add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${all_sources}) 12 | include(../../mintomic/cmake/BuildSettings.cmake) 13 | 14 | # Libraries 15 | add_subdirectory(../../mintomic/src/mintomic mintomic) 16 | add_subdirectory(../../mintomic/src/mintsystem mintsystem) 17 | add_subdirectory(../../mintomic/src/mintpack mintpack) 18 | include_directories(../../mintomic/include) 19 | add_subdirectory(../../common common) 20 | include_directories(../..) 21 | 22 | # Add libraries with dependencies after dependents to satisfy ld linker. 23 | target_link_libraries(${PROJECT_NAME} Common) 24 | target_link_libraries(${PROJECT_NAME} MintPack) 25 | target_link_libraries(${PROJECT_NAME} MintSystem) 26 | get_target_property(TARGET_TYPE Mintomic TYPE) 27 | if(${TARGET_TYPE} STREQUAL "UTILITY") 28 | # Nothing to link with in the Mintomic subdirectory 29 | else() 30 | target_link_libraries(${PROJECT_NAME} Mintomic) 31 | endif() 32 | -------------------------------------------------------------------------------- /projects/hashtable1/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | int main() 10 | { 11 | mint_timer_initialize(); 12 | 13 | std::vector exps; 14 | CollectionTest1::Plugin handler; 15 | exps.push_back(CollectionTest1::CreateExperiment(&handler, 12000, 16384)); 16 | CollectionTest2::Plugin handler2; 17 | exps.push_back(CollectionTest2::CreateExperiment(&handler2, 12000, 16384)); 18 | 19 | LoopForever(exps); 20 | 21 | for (size_t i = 0; i < exps.size(); i++) 22 | delete exps[i]; 23 | return 0; 24 | } 25 | --------------------------------------------------------------------------------