On 05/31/2013 01:05 PM, Timon Gehr wrote: > That wouldn't make any sense though, since after template expansion there is > no > difference between the generated version and a particular handwritten version.
That's what I'd assumed too, but there _is_ a speed difference. I'm open to suggestions as to why. Compare these profile results for the core inner function -- the original (even though there's a 'New' in there somewhere): % cumulative self self total time seconds seconds calls s/call s/call name 93.33 4.69 4.69 4084637 0.00 0.00 _D8infected5model115__T13NewSimulationS248infected5model8StateSISS238infected5model7SeedSISS388infected5model21NewUpdateMeanFieldSISTdZ13NewSimulation19__T11UpdateStateTdZ6updateMFKC8infected5model15__T8StateSISTdZ8StateSISKxC8infected5model14__T7SeedSISTdZ7SeedSISZd ... and the newer version: % cumulative self self total time seconds seconds calls s/call s/call name 92.73 5.23 5.23 4078287 0.00 0.00 _D8infected5model292__T10SimulationS358infected5model18UpdateMeanFieldSISTC8infected5model15__T8StateSISTdZ8StateSISTC8infected5model164__T7SeedSISTdTAyAS3std8typecons50__T5TupleTmVAyaa2_6964TdVAyaa9_696e666c75656e6365Z5TupleTyS3std8typecons50__T5TupleTmVAyaa2_6964TdVAyaa9_696e666c75656e6365Z5TupleZ7SeedSISTydZ10Simulation17__T11UpdateStateZ163__T6updateTdTAyAS3std8typecons50__T5TupleTmVAyaa2_6964TdVAyaa9_696e666c75656e6365Z5TupleTyS3std8typecons50__T5TupleTmVAyaa2_6964TdVAyaa9_696e666c75656e6365Z5TupleZ6updateMFNbNfKC8infected5model15__T8StateSISTdZ8StateSISKxC8infected5model164__T7SeedSISTdTAyAS3std8typecons50__T5TupleTmVAyaa2_6964TdVAyaa9_696e666c75656e6365Z5TupleTyS3std8typecons50__T5TupleTmVAyaa2_6964TdVAyaa9_696e666c75656e6365Z5TupleZ7SeedSISZd I'm not sure what, other than a change of template design, could be responsible here. The key bits of code follow -- the original version: mixin template UpdateState(T) { T update(ref StateSIS!T st, const ref SeedSIS!T sd) { T d = to!T(0); static T[] sick; sick.length = st.infected.length; sick[] = st.infected[]; foreach(i; 0..sick.length) { T noTransmission = to!T(1); foreach(link; sd.network[i]) noTransmission *= (to!T(1) - sick[link.id] * link.influence); T getSick = (to!T(1) - sick[i]) * (sd.susceptible[i] + (to!T(1) - sd.susceptible[i]) * (to!T(1) - noTransmission)); T staySick = sick[i] * (to!T(1) - sd.recover[i]); st.infected[i] = (to!T(1) - sd.immune[i]) * (getSick + staySick); assert(to!T(0) <= st.infected[i]); assert(st.infected[i] <= to!T(1)); d = max(abs(st.infected[i] - sick[i]), d); } return d; } } ... and for clarity, the StateSIS and SeedSIS classes: class StateSIS(T) { T[] infected; this(){} this(T[] inf) { infected = inf; } auto size() @property pure const nothrow { return infected.length; } T infection() @property pure const nothrow { return reduce!"a+b"(to!T(0), infected); } } class SeedSIS(T) { T[] immune; T[] susceptible; T[] recover; Link!T[][] network; this() {} this(T[] imm, T[] sus, T[] rec, Link!T[][] net) { immune = imm; susceptible = sus; recover = rec; network = net; } auto size() @property pure const nothrow in { assert(immune.length == susceptible.length); assert(immune.length == recover.length); assert(immune.length == network.length); } body { return immune.length; } } ... and the "Link" template: template Link(T) { alias Tuple!(size_t, "id", T, "influence") Link; } ... and now for comparison the new versions: mixin template UpdateState() { T update(T, N : L[][], L)(ref StateSIS!T st, const ref SeedSIS!(T, N, L) sd) { T d = to!T(0); static T[] sick; sick.length = st.infected.length; sick[] = st.infected[]; foreach(i; 0..sick.length) { T noTransmission = to!T(1); foreach(link; sd.network[i]) noTransmission *= (to!T(1) - sick[link.id] * link.influence); T getSick = (to!T(1) - sick[i]) * (sd.susceptible[i] + (to!T(1) - sd.susceptible[i]) * (to!T(1) - noTransmission)); T staySick = sick[i] * (to!T(1) - sd.recover[i]); st.infected[i] = (to!T(1) - sd.immune[i]) * (getSick + staySick); assert(to!T(0) <= st.infected[i]); assert(st.infected[i] <= to!T(1)); d = max(abs(st.infected[i] - sick[i]), d); } return d; } } class StateSIS(T) { T[] infected; this() {} this(T[] inf) { infected = inf; } auto size() @property pure const nothrow { return infected.length; } T infection() @property pure const nothrow { return reduce!"a+b"(to!T(0), infected); } } auto stateSIS(T)(T[] inf) { return new StateSIS!T(inf); } class SeedSIS(T, Network : L[][], L) { T[] immune; T[] susceptible; T[] recover; Network network; this() {} this(T[] imm, T[] sus, T[] rec, Network net) { immune = imm; susceptible = sus; recover = rec; network = net; } auto size() @property pure const nothrow in { assert(immune.length == susceptible.length); assert(immune.length == recover.length); assert(immune.length == network.length); } body { return immune.length; } } auto seedSIS(T, Network : L[][], L)(T[] imm, T[] sus, T[] rec, Network net) { return new SeedSIS!(T, Network, L)(imm, sus, rec, net); } ... note that the Network that is passed to SeedSIS is still always a Link!T[][].