├── .gitignore ├── 1.png ├── 2.png ├── cppgo.vcxproj ├── go.cpp ├── go.h ├── main.cpp └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | /Debug 2 | /Release 3 | /*.vcxproj 4 | /*.filters 5 | /*.user -------------------------------------------------------------------------------- /1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsZVA/cppgo/ea84eee9fbd15e5562aba70f22bdc458e68d84f5/1.png -------------------------------------------------------------------------------- /2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsZVA/cppgo/ea84eee9fbd15e5562aba70f22bdc458e68d84f5/2.png -------------------------------------------------------------------------------- /cppgo.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {E848081F-3C25-4CD7-AE1F-5307C036318F} 23 | cppgo 24 | 8.1 25 | 26 | 27 | 28 | Application 29 | true 30 | v140 31 | MultiByte 32 | 33 | 34 | Application 35 | false 36 | v140 37 | true 38 | MultiByte 39 | 40 | 41 | Application 42 | true 43 | v140 44 | MultiByte 45 | 46 | 47 | Application 48 | false 49 | v140 50 | true 51 | MultiByte 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Level3 75 | Disabled 76 | true 77 | 78 | 79 | 80 | 81 | Level3 82 | Disabled 83 | true 84 | 85 | 86 | 87 | 88 | Level3 89 | MaxSpeed 90 | true 91 | true 92 | true 93 | 94 | 95 | true 96 | true 97 | 98 | 99 | 100 | 101 | Level3 102 | MaxSpeed 103 | true 104 | true 105 | true 106 | 107 | 108 | true 109 | true 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /go.cpp: -------------------------------------------------------------------------------- 1 | #include "go.h" 2 | 3 | namespace go { 4 | volatile unsigned int nmspinning = 0; 5 | RWMutex nmspinning_lock; 6 | const unsigned int MAX_PROCS = 8; 7 | unsigned newP = 0; 8 | unsigned newM = 0; 9 | 10 | Queue gfpqueue; 11 | Queue tqueue; 12 | 13 | std::vector gplist; 14 | //std::mutex gplist_mutex; 15 | //std::unique_lock gplist_lock(gplist_mutex); 16 | std::mutex gplist_lock; 17 | 18 | template void debugPrint(T t) { 19 | #ifdef DEBUG //Because the empty function will be optimized by compiler 20 | std::cout << t << std::endl; 21 | #endif 22 | } 23 | 24 | void addnmspinning() { 25 | nmspinning_lock.wLock(); 26 | nmspinning++; 27 | nmspinning_lock.wUnlock(); 28 | } 29 | 30 | void minusnmspinning() { 31 | nmspinning_lock.wLock(); 32 | nmspinning--; 33 | nmspinning_lock.wUnlock(); 34 | } 35 | 36 | bool steal(P* p, P* p2) { 37 | auto ng = p2->gqueue.queue.size(); 38 | if (ng == 0) 39 | return false; 40 | else { 41 | ng = p2->gqueue.queue.size(); 42 | if (ng == 0) 43 | return false; 44 | for (unsigned i = 0;i <= ng / 2;i++) 45 | { 46 | p->gqueue.push(p2->gqueue.get()); 47 | } 48 | } 49 | debugPrint("steal success"); 50 | return true; 51 | } 52 | 53 | void spin(M* m) { 54 | 55 | debugPrint("spinning..."); 56 | addnmspinning(); 57 | nmspinning_lock.rLock(); 58 | while (nmspinning == 1) 59 | { 60 | nmspinning_lock.rUnLock(); //spinning 61 | nmspinning_lock.rLock(); 62 | } 63 | nmspinning_lock.rUnLock(); 64 | minusnmspinning(); 65 | debugPrint("a m spin -> nonspin"); 66 | while (1) { 67 | start_while: 68 | if (m->g != nullptr) { 69 | debugPrint("working..."); 70 | m->g->func(*(m->g->args)); 71 | delete m->g; 72 | m->g = nullptr; 73 | continue; 74 | } 75 | if (m->p == nullptr) { 76 | debugPrint("find a new p to m"); 77 | auto nfp = gfpqueue.queue.size(); 78 | if (nfp == 0) { 79 | debugPrint("a m go park"); 80 | delete m; 81 | break; 82 | } 83 | else { 84 | auto p = gfpqueue.get(); 85 | m->p = p; 86 | p->free = false; 87 | continue; 88 | } 89 | } 90 | if (m->g == nullptr) { 91 | debugPrint("find a new g to m"); 92 | auto ng = m->p->gqueue.queue.size(); 93 | if (ng == 0) { 94 | auto np = gplist.size(); 95 | if (np == 0) { 96 | m->p = nullptr; 97 | delete m; 98 | debugPrint("a m go park"); 99 | break; 100 | }; 101 | auto i = rand() % np; 102 | if (steal(m->p, gplist[i])) { 103 | continue; 104 | } 105 | else { 106 | for (int j = 0;j < gplist.size();j++) { 107 | i = (i + 1) % gplist.size(); 108 | if (steal(m->p, gplist[i])) { 109 | goto start_while; 110 | } 111 | } 112 | 113 | //gfpqueue.push(m->p); 114 | gfpqueue.lock.lock(); 115 | gfpqueue.queue.push(m->p); 116 | m->p->free = true; 117 | gfpqueue.lock.unlock(); 118 | 119 | debugPrint("p steal faild and go free"); 120 | m->p = nullptr; 121 | delete m; 122 | debugPrint("a m go park"); 123 | break; 124 | } 125 | } 126 | else { 127 | auto g = m->p->gqueue.get(); 128 | m->g = g; 129 | continue; 130 | } 131 | } 132 | } 133 | 134 | 135 | } 136 | 137 | void gocommit(G* g) { 138 | debugPrint("new g commit"); 139 | auto np = gplist.size(); 140 | P* p = nullptr; 141 | if (np == 0) { 142 | p = new P; 143 | newP++; 144 | debugPrint("new p"); 145 | gplist_lock.lock(); 146 | gplist.push_back(p); 147 | gplist_lock.unlock(); 148 | p->gqueue.push(g); 149 | gfpqueue.push(p); 150 | debugPrint("a p go to gfpqueue"); 151 | } 152 | else { 153 | //TODO find a runnable p 154 | auto np = gplist.size(); 155 | auto i = rand() % np; 156 | while (gplist[i]->free) 157 | i = (i + 1) % gplist.size(); 158 | gplist[i]->gqueue.queue.push(g); 159 | } 160 | np = gplist.size(); 161 | if (np < MAX_PROCS && nmspinning == 1) { 162 | auto m = new M; 163 | newM++; 164 | debugPrint("new m"); 165 | if (p == nullptr) { 166 | m->p = new P; 167 | newP++; 168 | } 169 | else if (gfpqueue.queue.size() == 0) { 170 | m->p = new P; 171 | newP++; 172 | } 173 | else { 174 | m->p = gfpqueue.get(); 175 | } 176 | debugPrint("new p"); 177 | gplist_lock.lock(); 178 | gplist.push_back(m->p); 179 | gplist_lock.unlock(); 180 | auto t = new std::thread (spin, m); 181 | //t->detach(); 182 | tqueue.push(t); 183 | } 184 | } 185 | 186 | void goinit() { 187 | auto m = new M; 188 | newM++; 189 | debugPrint("new m"); 190 | auto t = new std::thread(spin, m); 191 | //t->detach(); 192 | tqueue.push(t); 193 | } 194 | 195 | void goend() { 196 | while (!tqueue.queue.empty()) { 197 | auto t = tqueue.get(); 198 | t->join(); 199 | } 200 | } 201 | } -------------------------------------------------------------------------------- /go.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace go { 14 | 15 | class RWMutex { 16 | private: 17 | 18 | std::mutex wm, rmm; 19 | std::unique_lock rmtx; 20 | std::condition_variable cond; 21 | int rd_cnt = 0; //wait for read 22 | bool wr_cnt = false; //wait for write 23 | public: 24 | RWMutex() : rmtx(rmm, std::defer_lock) {} 25 | void rLock() { 26 | while (wr_cnt) 27 | cond.wait(rmtx); 28 | } 29 | 30 | void rUnLock() { 31 | if (!wr_cnt) 32 | cond.notify_all(); 33 | } 34 | 35 | void wLock() { 36 | wm.lock(); 37 | while (wr_cnt) 38 | cond.wait(rmtx); 39 | wr_cnt = true; 40 | wm.unlock(); 41 | } 42 | 43 | void wUnlock() { 44 | wm.lock(); 45 | wr_cnt = false; 46 | cond.notify_all(); 47 | wm.unlock(); 48 | } 49 | }; 50 | 51 | template struct Queue { 52 | std::queue queue; 53 | std::mutex mutex; 54 | std::unique_lock lock; 55 | //std::mutex lock; 56 | Queue():lock(mutex, std::defer_lock){} 57 | void push(T t) { 58 | lock.lock(); 59 | queue.push(t); 60 | lock.unlock(); 61 | } 62 | T get() { 63 | auto ret = queue.front(); 64 | lock.lock(); 65 | queue.pop(); 66 | lock.unlock(); 67 | return ret; 68 | } 69 | }; 70 | 71 | template struct Map { 72 | std::map map; 73 | RWMutex mutex; 74 | void set(S s, T t) { 75 | mutex.wLock(); 76 | map[s] = t; 77 | mutex.wUnlock(); 78 | } 79 | T get(S s) { 80 | mutex.rLock(); 81 | auto ret = map[s]; 82 | mutex.rUnLock(); 83 | return ret; 84 | } 85 | void remove(S s) { 86 | mutex.wLock(); 87 | map.erase(s); 88 | mutex.wUnlock(); 89 | } 90 | unsigned int size() { 91 | mutex.rLock(); 92 | auto ret = map.size(); 93 | mutex.rUnLock(); 94 | return ret; 95 | } 96 | }; 97 | 98 | typedef std::map args_type; 99 | typedef void* (*func_type)(args_type args); 100 | 101 | 102 | typedef struct _g{ 103 | func_type func = nullptr; 104 | args_type* args = nullptr; 105 | } G; 106 | 107 | typedef struct _p { 108 | Queue gqueue; 109 | bool free = false; 110 | } P; 111 | 112 | typedef struct _m { 113 | P* p = nullptr; 114 | G* g = nullptr; 115 | } M; 116 | 117 | void gocommit(G* g); 118 | void goinit(); 119 | void spin(M* m); 120 | bool steal(P* p, P* p2); 121 | void minusnmspinning(); 122 | void addnmspinning(); 123 | void goend(); 124 | } 125 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "go.h" 3 | #include "windows.h" 4 | 5 | int i = 0; 6 | std::mutex m; 7 | 8 | void* job(go::args_type args) { 9 | std::cout << "jobjobjobjobjobjobjobjobjobjobjobjobjobjobjobjob" << std::endl; 10 | m.lock(); 11 | i++; 12 | m.unlock(); 13 | int j = 0; 14 | for (int i = 0;i < 10000000;i++) 15 | j = j + rand() % 10; 16 | std::cout << j << std::endl; 17 | return nullptr; 18 | } 19 | 20 | 21 | int main() { 22 | 23 | go::goinit(); 24 | for (int i = 0;i < 30;i++) { 25 | auto g = new go::G; 26 | g->func = job; 27 | g->args = new std::map(); 28 | go::gocommit(g); 29 | } 30 | //while (1); 31 | go::goend(); 32 | /* 33 | for (int i = 0;i < 30;i++) { 34 | std::thread t(job, std::map()); 35 | t.detach(); 36 | } 37 | while (1);*/ 38 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #CppGo 2 | 3 | ## Destination 4 | 5 | Make cpp can use goroutine, which is a light thread well scheduled that both parralization and power-saving has been thoughted. 6 | 7 | ## Compare 8 | 9 | Compare to use std::thread directly: 10 | 11 | ![std::thread * 30](1.png) 12 | std::thread * 30 13 | 14 | ![go::gocommit * 30](2.png) 15 | go::commit * 30 16 | 17 | Intel i7 - 4712MQ 8Cores 18 | 19 | ## Feather 20 | 21 | Compared to use thread whenever there is a job, it will cost less to switch between threads (because there are at most cores number of 22 | working thread in cppgo); Compared to use thread pool, it can mostly parralize when a specify thread block (the context P will move to 23 | another working thread). --------------------------------------------------------------------------------