From 726cc6394fba1186657642746f6baa365ccffeb8 Mon Sep 17 00:00:00 2001 From: Richard Feistenauer Date: Sun, 10 Feb 2019 11:54:42 +0100 Subject: [PATCH] added single and multi core deviation --- scripts/main_ui.py | 6 +- scripts/performance_test | Bin 3861 -> 2456 bytes src/cellular_automaton/ca_cell_state.py | 66 +++++++++++++------ src/cellular_automaton/cellular_automaton.py | 19 ++++-- 4 files changed, 61 insertions(+), 30 deletions(-) diff --git a/scripts/main_ui.py b/scripts/main_ui.py index baad7a5..55d371a 100644 --- a/scripts/main_ui.py +++ b/scripts/main_ui.py @@ -43,8 +43,8 @@ if __name__ == "__main__": random.seed(1000) # best single is 400/400 with 0,2 ca speed and 0,09 redraw / multi is 300/300 with 0.083 neighborhood = MooreNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS) - ca = make_cellular_automaton(dimension=[100, 100], neighborhood=neighborhood, rule=TestRule(), state_class=MyState) - ca_processor = CellularAutomatonProcessor(process_count=4, cellular_automaton=ca) + ca = make_cellular_automaton(dimension=[400, 400], neighborhood=neighborhood, rule=TestRule(), state_class=MyState) + ca_processor = CellularAutomatonProcessor(process_count=1, cellular_automaton=ca) ca_window = PyGameFor2D(window_size=[1000, 800], cellular_automaton=ca) - ca_window.main_loop(cellular_automaton_processor=ca_processor, ca_iterations_per_draw=10) + ca_window.main_loop(cellular_automaton_processor=ca_processor, ca_iterations_per_draw=1) diff --git a/scripts/performance_test b/scripts/performance_test index 7ab56b929eddf51980b0589ee55d64115ede0b85..0bc20ef77058ac4d0349b669361d5c7cb21298cb 100644 GIT binary patch literal 2456 zcmcImdrTZf9A4y63gso`Q7HC`P&lm|F{A+k3<5^cq*1GuCRaCmdvn7yd%JXYk3z7e z{zqa$jjy)R+GvnGXVFg=twzg_kop7LM+`DJeN z&G&u3$IRTkGcoj3YrX3k9W&kKQ36<00;=R{)8v33tA_4+f@y?Fem_--tE<=O)KqbY z#uTqeu!@;vXw0R{jCOITU!tlQpjBPH!R3M=Os3;*P3gv>j1|S1OpU{df*DA9;|u$q z?t2G?7k%$1hNRYdw`s#2w&4!ja7Wp2|F;dd&SW=81r&*iQozvs+@0!{CDQ=7N(Wb2 zi>|0oWLWa{I+K=iR_#n=$$VQdi-%BJgFf)cXAWb-y7}nmI7kGML-k$9tvRsUpSI4eC|P zJxykdT}+d)u8T|y7*xePt0i3WtU}~YxwKj->V{-sFqjVpOLT0Al59{uYCesLD(*2v zYQS((P31M+w;@R|1Nm`oZpFo$&m<^s)fsxSJ9Y3Sw=Z|xg{(}+~={kHPpV>eKENFwEo!-SdF0CO?n2F>h6 z%_l079-HXO&m01UK#t~J)Ae?)^(pm@=ZuCBA09JNmdbe;98aFnq2dz33}j*A@w1K7 zQ;5twx%=|MM}MOD`EAd*aQs8(2qK=d)vmqGU!kzIQm~dEV=cP;1D-O0LLi%7TYnoD z+(0D%<(J-c(Oae~s2j_J0Nv!SB3YF8D=qc(IwHZJ^n{LAW{_Rx8c@s-(1acP&9+5^y-l|!3<>A(4}|ZQuvi*FJJ=9T4#bExX!p%WTv0XJ@M?XhYulQ zFKjrBG+SxGBZJzu=hzonGu*ztu;B#&FSOv1&JULa`)>s%5V04m*n&iQc4^ZiQ(Ke+ zh}a8A7Oq~F0|5?bOwUG+ysV;dGT;b4sjtWuH z`Ag`l;(tYaRKbjY^=!kkq4V*Hvbo_*IZM5Mq>0#zz8H3PKbMHvaP{~v^P7+6A#&Hr L+hrY)S9SG2Y?UhM literal 3861 zcmbtXYitx%6fQ1(^Qb6jh0+BrwLI2_5;T;|RKREi42F=PS)EL0Z*K=?XO=s&eWXzg zF(M6GLWqXi#Ar0i4@DF(kz(Sj5+VsfgSr%fgh!V~1R)@c0ngppxifcWOGx~4dw1r$ z-*?Wr=bSmcQ5VWAU%Vi?%0$GBHfu>FX-Qp5BkOl}ZxQEkvk_=hSg)%`2rp{4U^SJ=-D& z>dy*HWzt=6YIM7-TAWPa0F${)WxyBIVfzwLNeg)9>C1iP>;DqENQvKmo7jV4#MdeD zScxDF$tIFaxeev_;Z}9|gn_n`t`8+NQlVhk6_Tv1OR5BcqG=g~7wc>w zA3gc)X#xK@;p$gsoBjq@#`y&T7$$Iw3Y1E!DH-iLig9lA6rNIw%YPAYczExgr`|o6 zk2!#Gm{r$R@AZ~#T6D2ya)pRD9ezgnLfI$g348#3p{S7+~h zv+Vt!1ng|Opg``03sKk8R4=4u*~#U)17s(d2rUjfvAmJ#y;-&N;Q4Y9zp-V0;~%XM zZAW1v!*eC;#{+*x%M-~ZhLVg!`9f2%yGuM9p=^VZ+6|(`B#d-KWJuSjNtCF9G%cyg zM3R%15r@Zmo=3C+O`!=n4z(Ft8KRrFpvA-MesSt+B^704pjNi?BFaMRjKR2`- zft)&UXuCYD!ZncT{+I+XjFZ|x9`xIMb7#fmy3hX+@Vv;|TYELiInB=*z%WiXZeBZA zH)blgFKQ6+#HD4o8W%UY_eigwg#G*hJmsGZD7z|XtwEgK3K?47x2h=ejez^MUHy2s zU0az^FQ)-lkf~nSoK!Vyj;e>_2quYGIQv`G%?Rp13SVmhCaPRRm1j_{`h_`)S~-zG zdaO2Vw1jIFL(`CAsRmHS(7Ggo$IWZti79aDV1IPJ7qGL*^T~5wG0aE?#8gvm)~FO% zAZaFa{FVrGwH8%Fgfqa6)BOE4q-5wV0Xv&6o_)6Kp8e&{vD>3oL^G7NUgq?}G%&MZ z!2^rWJ~r!xeInb?jtzHPK*rOh0v0aQ?fK<|Gsv6}9QfT7KEV(F?C|E|?uKImb~d?L zAx(P}nGFm6ZdZ@q6a@>M%}B20#-H1}=9#(dk5|9*x$760Yh$SRSih|SJa0&Z#h49} zcWg@kj;{w_>pLP~XTwY>qNWu4O>tlh&zmyGYf5(5ykkr31R8KMX}}FicJMCuTN1$Y zmedAnRLbR?tU=bv7W{_*$w$kHUbcPXrKb+Qw7?o8VrP@5oa_(Juz;NnDfFAB$f4nl zorNNHHoR%m0;aL{5mVdXd(GFl?dVY_Fh^nde8{gDz;KGBmg2qbtw)_c<7DL|5zntG znAW%(S~7PaGks(eTV+$WEW+DpJ9-92k*w=y^4aLTVi;a>>d2BB(y1J63wCd?3u4eI zpg~veHW*&+Ha%QN9Tx9iVA|{=_xP_MfZ_GrB26T%Tv0$@Pol_5HJ;GWvvd#7^L6>5 Xg)dl!E}f% diff --git a/src/cellular_automaton/ca_cell_state.py b/src/cellular_automaton/ca_cell_state.py index 4aa0020..bc70f78 100644 --- a/src/cellular_automaton/ca_cell_state.py +++ b/src/cellular_automaton/ca_cell_state.py @@ -3,31 +3,30 @@ from ctypes import c_float, c_bool class CellState: - _state_save_slot_count = 2 """ This is the base class for all cell states. When using the cellular automaton display, inherit this class and implement get_state_draw_color. """ + + _state_save_slot_count = 2 + def __init__(self, initial_state=(0., ), draw_first_state=True): - self._state_slots = [RawArray(c_float, initial_state) for i in range(self.__class__._state_save_slot_count)] - self._active = [RawValue(c_bool, False) for i in range(self.__class__._state_save_slot_count)] - self._active[0].value = True - if draw_first_state: - self._dirty = RawValue(c_bool, True) - else: - self._dirty = RawValue(c_bool, False) + self._state_slots = [list(initial_state) for i in range(self.__class__._state_save_slot_count)] + self._active = [False for i in range(self.__class__._state_save_slot_count)] + self._active[0] = True + self._dirty = draw_first_state def is_active(self, iteration): - return self._active[self.__calculate_slot(iteration)] + return self._active[self._calculate_slot(iteration)] def set_active_for_next_iteration(self, iteration): - self._active[self.__calculate_slot(iteration + 1)].value = True + self._active[self._calculate_slot(iteration + 1)] = True def is_set_for_redraw(self): - return self._dirty.value + return self._dirty def was_redrawn(self): - self._dirty.value = False + self._dirty = False def get_state_of_last_iteration(self, current_iteration_index): return self.get_state_of_iteration(current_iteration_index - 1) @@ -37,7 +36,7 @@ class CellState: :param iteration: Uses the iteration index, to differ between concurrent states. :return The state for this iteration. """ - return self._state_slots[self.__calculate_slot(iteration)] + return self._state_slots[self._calculate_slot(iteration)] def set_state_of_iteration(self, new_state, iteration): """ Will set the new state for the iteration modulo number of saved states. @@ -45,25 +44,50 @@ class CellState: :param iteration: Uses the iteration index, to differ between concurrent states. :return True if state has changed. """ + changed = self._change_state(new_state, iteration) + self._dirty |= changed + self._active[self._calculate_slot(iteration)] = False + return changed + def _change_state(self, new_state, iteration): current_state = self.get_state_of_iteration(iteration) - changed = False - for i in range(len(current_state)): + for i, ns in enumerate(new_state): try: - if current_state[i] != new_state[i]: + if current_state[i] != ns: changed = True - current_state[i] = new_state[i] + current_state[i] = ns except IndexError: raise IndexError("New State length or type is invalid!") - - self._dirty.value |= changed - self._active[self.__calculate_slot(iteration)].value = False return changed def get_state_draw_color(self, iteration): raise NotImplementedError @classmethod - def __calculate_slot(cls, iteration): + def _calculate_slot(cls, iteration): return iteration % cls._state_save_slot_count + + +class SynchronousCellState(CellState): + def __init__(self, initial_state=(0., ), draw_first_state=True): + super().__init__(initial_state, draw_first_state) + self._state_slots = [RawArray(c_float, initial_state) for i in range(self.__class__._state_save_slot_count)] + self._active = [RawValue(c_bool, False) for i in range(self.__class__._state_save_slot_count)] + self._active[0].value = True + self._dirty = RawValue(c_bool, draw_first_state) + + def set_active_for_next_iteration(self, iteration): + self._active[self._calculate_slot(iteration + 1)].value = True + + def is_set_for_redraw(self): + return self._dirty.value + + def was_redrawn(self): + self._dirty.value = False + + def set_state_of_iteration(self, new_state, iteration): + changed = self._change_state(new_state, iteration) + self._dirty.value |= changed + self._active[self._calculate_slot(iteration)].value = False + return changed diff --git a/src/cellular_automaton/cellular_automaton.py b/src/cellular_automaton/cellular_automaton.py index c258fdd..e745107 100644 --- a/src/cellular_automaton/cellular_automaton.py +++ b/src/cellular_automaton/cellular_automaton.py @@ -17,11 +17,18 @@ class CellularAutomatonProcessor: self.ca = cellular_automaton cells = {i: (c.state, c.neighbours) for i, c in enumerate(self.ca.cells)} self.evolve_range = range(len(self.ca.cells)) - self.pool = multiprocessing.Pool(processes=process_count, - initializer=_init_process, - initargs=(cells, - self.ca.evolution_rule, - self.ca.evolution_iteration_index)) + self._evolve_method = lambda x, y: None + + if process_count > 1: + self.pool = multiprocessing.Pool(processes=process_count, + initializer=_init_process, + initargs=(cells, + self.ca.evolution_rule, + self.ca.evolution_iteration_index)) + self._evolve_method = self.pool.map + else: + _init_process(cells, self.ca.evolution_rule, self.ca.evolution_iteration_index) + self._evolve_method = lambda x, y: list(map(x, y)) for cell in self.ca.cells: del cell.neighbours @@ -31,7 +38,7 @@ class CellularAutomatonProcessor: def evolve(self): self.ca.evolution_iteration_index.value += 1 - self.pool.map(_process_routine, self.evolve_range) + self._evolve_method(_process_routine, self.evolve_range) global_cells = None