From fef0c05b649ca2785a464ef7aab271946b4cf02a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 30 Apr 2023 22:33:42 +0200 Subject: [PATCH] Job-Planning: base implementation of job instance creation * using a simplified preliminary implementation of hash chaining (see #1293) * simplistic implementation of hashing for time values (half-rotation) * for now just hashing the time into the upper part of the LUID Maybe we can even live with that implementation for some time, depending on how important uniform distribution of hash values is for proper usage of the frame cache. Needless to say, various further fine points need more consideration, especially questions of portability (32bit anyone?). Moreover, since frame times are typically quantised, the search space for the hashed time values is drastically reduced; conceivably we should rather research and implement a good hash function for 128bit and then combine all information into a single hash key.... --- src/lib/time/timevalue.hpp | 20 +++++++++ src/steam/engine/job-ticket.cpp | 30 ++++++++++++- src/steam/engine/job-ticket.hpp | 7 ++- src/vault/engine/job.h | 6 ++- tests/basics/time/time-value-test.cpp | 20 +++++++++ tests/core/vault/engine/dummy-job.cpp | 55 ++++++++++++++--------- tests/core/vault/engine/job-hash-test.cpp | 4 +- wiki/thinkPad.ichthyo.mm | 41 +++++++++++++++++ 8 files changed, 155 insertions(+), 28 deletions(-) diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 0e9548eae..08d3d96a6 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -188,6 +188,7 @@ namespace time { /** @internal to pass Time values to C functions */ friend gavl_time_t _raw (TimeValue const& time) { return time.t_; } + friend HashVal hash_value (TimeValue const&); static TimeValue buildRaw_(gavl_time_t); /** @internal diagnostics */ @@ -708,6 +709,25 @@ namespace time { + + /** derive a hash from the ยต-tick value + * @return rotation of the raw value to produce a suitable spacing for consecutive time + * @remark picked up by Boost-hash, or std. hashtables with the help of `hash-standard.h` + * @see https://stackoverflow.com/a/31488147 + */ + inline HashVal + hash_value (TimeValue const& time) + { + HashVal x = _raw(time); // possibly cap to size of hash + const uint width = sizeof(HashVal) * CHAR_BIT; + const uint mask = width-1; + const uint n = width / 2; + + static_assert (0 < n and n <= mask); + return (x<>((-n)&mask )); + } + + /** @internal applies a limiter on the provided * raw time value to keep it within the arbitrary * boundaries defined by (Time::MAX, Time::MIN). diff --git a/src/steam/engine/job-ticket.cpp b/src/steam/engine/job-ticket.cpp index 48f838436..65783b028 100644 --- a/src/steam/engine/job-ticket.cpp +++ b/src/steam/engine/job-ticket.cpp @@ -108,8 +108,34 @@ using lib::HashVal; Job JobTicket::createJobFor (FrameCoord coordinates) { - REQUIRE (this->isValid(), "Attempt to generate render job for incomplete or unspecified render plan."); - UNIMPLEMENTED ("job planning and generation"); + if (isnil (provision_)) + { + + } + else + { + REQUIRE (this->isValid(), "Attempt to generate render job for incomplete or unspecified render plan."); + REQUIRE (coordinates.channelNr < provision_.size(), "Inconsistent Job planning; channel beyond provision"); + Provision& provision = provision_[coordinates.channelNr]; + JobClosure& functor = static_cast (provision.jobFunctor); /////////////////////////TICKET #1295 : fix actual interface down to JobFunctor (after removing C structs) + Time nominalTime = coordinates.absoluteNominalTime; + InvocationInstanceID invoKey{timeHash (nominalTime, provision.invocationSeed)}; + + return Job(functor, invoKey, nominalTime); + } + } + + /** + * Tag the precomputed invocation ID with the nominal frame time + */ + InvocationInstanceID + JobTicket::timeHash (Time nominalTime, InvocationInstanceID const& seed) + { + InvocationInstanceID res{seed}; + HashVal timeMark = res.part.t; + lib::hash::combine (timeMark, hash_value (nominalTime)); + res.part.t = timeMark; + return res; } diff --git a/src/steam/engine/job-ticket.hpp b/src/steam/engine/job-ticket.hpp index 193034bce..11057f149 100644 --- a/src/steam/engine/job-ticket.hpp +++ b/src/steam/engine/job-ticket.hpp @@ -61,10 +61,12 @@ namespace engine { //using lib::time::Time; using vault::engine::Job; using vault::engine::JobFunctor; +using vault::engine::JobClosure; /////////////////////////////////////////////////////////////////////TICKET #1295 : fix actual interface down to JobFunctor (after removing C structs) using lib::LinkedElements; using lib::OrientationIndicator; using util::isnil; using lib::HashVal; +using lib::LUID; // //class ExitNode; @@ -109,12 +111,12 @@ using lib::HashVal; { Provision* next{nullptr}; JobFunctor& jobFunctor; - HashVal invocationSeed; + InvocationInstanceID invocationSeed; Prerequisites requirements{}; ////////////////////TODO some channel or format descriptor here Provision (JobFunctor& func, HashVal seed =0) : jobFunctor{func} - , invocationSeed{seed} + , invocationSeed(static_cast(func).buildInstanceID(seed)) ////////////////TICKET #1295 : fix actual interface down to JobFunctor (after removing C structs) { } }; @@ -159,6 +161,7 @@ using lib::HashVal; } protected: + static InvocationInstanceID timeHash (Time, InvocationInstanceID const&); bool verifyInstance (JobFunctor&, Time) const; }; diff --git a/src/vault/engine/job.h b/src/vault/engine/job.h index 5fa2660f6..61137a989 100644 --- a/src/vault/engine/job.h +++ b/src/vault/engine/job.h @@ -122,8 +122,10 @@ union InvocationInstanceID lumiera_uid luid{0}; /* ----- alternative accessors for test code ---- */ - FrameCnt frameNumber; - struct {int a,b;} metaInfo; + FrameCnt frameNumber; + struct { int32_t a,b; + int64_t t; + } part; }; diff --git a/tests/basics/time/time-value-test.cpp b/tests/basics/time/time-value-test.cpp index db13725f6..fda2dcdc0 100644 --- a/tests/basics/time/time-value-test.cpp +++ b/tests/basics/time/time-value-test.cpp @@ -70,6 +70,7 @@ namespace test{ checkBasicTimeValues (ref); checkMutableTime (ref); + checkTimeHash (ref); checkTimeConvenience (ref); verify_invalidFramerateProtection(); createOffsets (ref); @@ -201,6 +202,25 @@ namespace test{ } + /** @test calculate a generic hash value from a time spec*/ + void + checkTimeHash (TimeValue org) + { + std::hash hashFunc; + CHECK (0 == hashFunc (Time::ZERO)); + size_t hh = sizeof(size_t)*CHAR_BIT/2; + CHECK (size_t(1)< 0 || org == Time::ZERO); + CHECK (h2 - h1 == size_t(1)<{} (invoKey.metaInfo.a); - } ////////////////////////////////////////////////////////////////////////TICKET #1293 : this is dangerous and could lead to clashes + std::hash hashr; + HashVal res = hashr (invoKey.frameNumber); + lib::hash::combine (res, hashr (invoKey.part.t)); + return res; + } /* === Logging/Reporting of job invocation === */ @@ -114,17 +129,17 @@ namespace engine { TimeVar real; int a,b; - Invocation(JobParameter param) - : nominal(TimeValue(param.nominalTime)) - , real(RealClock::now()) - , a(param.invoKey.metaInfo.a) - , b(param.invoKey.metaInfo.b) + Invocation (JobParameter param) + : nominal{TimeValue(param.nominalTime)} + , real{RealClock::now()} + , a{param.invoKey.part.a} + , b{param.invoKey.part.b} { } Invocation() - : nominal(Time::ANYTIME) - , real(Time::NEVER) - , a(MAX_PARAM_A), b(0) + : nominal{Time::ANYTIME} + , real{Time::NEVER} + , a{MAX_PARAM_A}, b{0} { } }; @@ -165,8 +180,8 @@ namespace engine { DummyJob::build() { InvocationInstanceID invoKey; - invoKey.metaInfo.a = rand() % MAX_PARAM_A; - invoKey.metaInfo.b = rand() % (2*MAX_PARAM_B) - MAX_PARAM_B; + invoKey.part.a = rand() % MAX_PARAM_A; + invoKey.part.b = rand() % (2*MAX_PARAM_B) - MAX_PARAM_B; Time nominalTime = lib::test::randTime(); @@ -178,8 +193,8 @@ namespace engine { DummyJob::build (Time nominalTime, int additionalKey) { InvocationInstanceID invoKey; - invoKey.metaInfo.a = additionalKey; - invoKey.metaInfo.b = rand() % (2*MAX_PARAM_B) - MAX_PARAM_B; + invoKey.part.a = additionalKey; + invoKey.part.b = rand() % (2*MAX_PARAM_B) - MAX_PARAM_B; return Job(dummyClosure, invoKey, nominalTime); } diff --git a/tests/core/vault/engine/job-hash-test.cpp b/tests/core/vault/engine/job-hash-test.cpp index 0a4a19860..26b75a637 100644 --- a/tests/core/vault/engine/job-hash-test.cpp +++ b/tests/core/vault/engine/job-hash-test.cpp @@ -99,7 +99,7 @@ namespace test { CHECK (hash_value(job1) != hash_value(copy)); // hash value depends on the concrete nominal job time copy = job1; - copy.parameter.invoKey.metaInfo.a++; + copy.parameter.invoKey.part.a++; CHECK (hash_value(job1) != hash_value(copy)); // hash value depends on the internal interpretation of the invocation key @@ -115,7 +115,7 @@ namespace test { size_t hashOfInstance(InvocationInstanceID invoKey) const { - return boost::hash_value (invoKey.metaInfo.a); + return boost::hash_value (invoKey.part.a); } }; OtherClosure someOtherClosure; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 9b72ee3ae..38291bfe4 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -69824,6 +69824,42 @@ + + + + + + + + +

+ Verwende eine halb-Rotation über size_t ⟹ +

+
    +
  • + die beiden Hälften des Bitstring der µ-ticks werden vertauscht +
  • +
  • + damit erheblicher Abstand zwischen konsekutiven Werten +
  • +
  • + hash² ≡ id +
  • +
  • + impl sollte i.d.R. nach inlining eine einzige Assembler-Instruktion sein ⟶ https://stackoverflow.com/a/31488147 +
  • +
+ +
+ +
+ + + + + + +
@@ -70696,6 +70732,11 @@ + + + + +