Optaplanner config with moveThreadCount=1 not same as no moveThreadCount - multithreading
I have upgraded to Optaplanner 7.12, and in hunting for a potential issue with multithreading mixed with VariableListeners, noticed an oddity in reproducible execution: if the configuration file has <moveThreadCount>1</moveThreadCount>, the execution is NOT the same as when the moveThreadCount line is absent, which seems unexpected to me as a user, and might be intertwined with the potential optaplanner race condition I am seeing (noted at the end of this post).
Code details
I observe this for a configuration file in REPRODUCIBLE mode with a fixed seed:
<environmentMode>REPRODUCIBLE</environmentMode>
<randomSeed>50</randomSeed>
The difference in Optaplanner behavior is visible during my use of a VariableListener. I have a set of custom MoveFactory classes for a nurse-rostering-derived model. Each custom factory generates a different set of moves for each STEP, and each decides on their moves based on a common set of state-dependent computationally intensive pre-calculations. I created a MoveFactoryHelper class that makes the precalculations, and then call the helper at the beginning of each custom MoveFactory's createMoveList method (I have not yet attempted to migrate to the newer Optaplanner iterative move generation option).
To avoid repeating the computations for each of the move factories, the MoveFactoryHelper stores its result for re-use, and decides when to re-compute based on a 'dirty' flag that is set in a VariableListener registered on a (completely unused) shadow to the model's PlanningEntity, and cleared by the MoveFactoryHelper when the re-computations occur:
ShiftAssignment.java
#PlanningEntity(movableEntitySelectionFilter = MovableShiftAssignmentSelectionFilter.class,
difficultyComparatorClass = ShiftAssignmentDifficultyComparator.class)
#XStreamAlias("ShiftAssignment")
public class ShiftAssignment extends AbstractPersistable {
...
#PlanningVariable(valueRangeProviderRefs = {"employeeRange"},
strengthComparatorClass = EmployeeStrengthComparator.class
)
private Employee employee;
...
#CustomShadowVariable( variableListenerClass=UpdatingEmployeeVariableListener.class,
sources={#PlanningVariableReference(variableName="employee", entityClass=ShiftAssignment.class)})
private Employee notifierEmployee; // TODO is there a better way to notify move factory of changes in problem facts?
UpdatingEmployeeVariableListener.java
private static final Logger logger = LoggerFactory.getLogger(UpdatingEmployeeVariableListener.class);
private static final boolean initiallyDirty = true;
private static Map<Thread, Boolean> employeeShiftAssignmentEntityDirty = new HashMap<Thread, Boolean>();
private static Map<Thread, Boolean> employeeShiftAssignmentMapDirty = new HashMap<Thread, Boolean>();
private static final boolean useThreadFlags = false;
// debug monitoring
private static Map<Thread, Integer> countDirtyAllFlags = new HashMap<Thread, Integer>();
private static Map<Thread, Integer> countBeforeEntityAdded = new HashMap<Thread, Integer>();
private static Map<Thread, Integer> countAfterEntityAdded = new HashMap<Thread, Integer>();
private static Map<Thread, Integer> countBeforeVariableChanged = new HashMap<Thread, Integer>();
private static Map<Thread, Integer> countAfterVariableChanged = new HashMap<Thread, Integer>();
private static Map<Thread, Integer> countBeforeEntityRemoved = new HashMap<Thread, Integer>();
private static Map<Thread, Integer> countAfterEntityRemoved = new HashMap<Thread, Integer>();
public UpdatingEmployeeVariableListener() {
// no action
}
private static Thread getActiveThread() {
return useThreadFlags ? Thread.currentThread() : null;
}
public static void setFlagsDirty() {
countDirtyAllFlags.put(getActiveThread(), 1+countDirtyAllFlags.getOrDefault(getActiveThread(), 0));
employeeShiftAssignmentEntityDirty.put(getActiveThread(), true);
employeeShiftAssignmentMapDirty.put(getActiveThread(), true);
}
#Override
public void beforeEntityAdded(#SuppressWarnings("rawtypes") ScoreDirector scoreDirector, ShiftAssignment entity) {
countBeforeEntityAdded.put(getActiveThread(), 1+countBeforeEntityAdded.getOrDefault(getActiveThread(), 0));
employeeShiftAssignmentMapDirty.put(getActiveThread(), true);
}
#Override
public void afterEntityAdded(#SuppressWarnings("rawtypes") ScoreDirector scoreDirector, ShiftAssignment entity) {
countAfterEntityAdded.put(getActiveThread(), 1+countAfterEntityAdded.getOrDefault(getActiveThread(), 0));
employeeShiftAssignmentMapDirty.put(getActiveThread(), true);
}
#Override
public void beforeVariableChanged(#SuppressWarnings("rawtypes") ScoreDirector scoreDirector,
ShiftAssignment entity) {
countBeforeVariableChanged.put(getActiveThread(), 1+countBeforeVariableChanged.getOrDefault(getActiveThread(), 0));
employeeShiftAssignmentMapDirty.put(getActiveThread(), true);
}
#Override
public void afterVariableChanged(#SuppressWarnings("rawtypes") ScoreDirector scoreDirector,
ShiftAssignment entity) {
countAfterVariableChanged.put(getActiveThread(), 1+countAfterVariableChanged.getOrDefault(getActiveThread(), 0));
employeeShiftAssignmentMapDirty.put(getActiveThread(), true);
}
#Override
public void beforeEntityRemoved(#SuppressWarnings("rawtypes") ScoreDirector scoreDirector, ShiftAssignment entity) {
countBeforeEntityRemoved.put(getActiveThread(), 1+countBeforeEntityRemoved.getOrDefault(getActiveThread(), 0));
employeeShiftAssignmentMapDirty.put(getActiveThread(), true);
}
#Override
public void afterEntityRemoved(#SuppressWarnings("rawtypes") ScoreDirector scoreDirector, ShiftAssignment entity) {
countAfterEntityRemoved.put(getActiveThread(), 1+countAfterEntityRemoved.getOrDefault(getActiveThread(), 0));
employeeShiftAssignmentMapDirty.put(getActiveThread(), true);
}
/**
* #return the employeeShiftAssignmentEntityDirty
*/
public static boolean isEmployeeShiftAssignmentEntityDirty() {
return employeeShiftAssignmentEntityDirty.getOrDefault(getActiveThread(), initiallyDirty);
}
/**
* clears isEntityDirty, implying that the (externally maintained) employee shift assignment entity list has been updated
*/
public static void clearEmployeeShiftAssignmentEntityDirty() {
employeeShiftAssignmentEntityDirty.put(getActiveThread(), false);
}
/**
* #return the mapDirty (which is depending also on entityDirty)
*/
public static boolean isEmployeeShiftAssignmentMapDirty() {
return employeeShiftAssignmentMapDirty.getOrDefault(getActiveThread(), initiallyDirty) || isEmployeeShiftAssignmentEntityDirty();
}
/**
* clears isMapDirty, implying that the (externally maintained) employee shift assignment map has been updated (as well as the underlying entity)
*/
public static void clearEmployeeShiftAssignmentMapDirty() {
clearEmployeeShiftAssignmentEntityDirty();
employeeShiftAssignmentMapDirty.put(getActiveThread(), false);
logger.debug("Clearing dirty flag: (AF={}, BEA={}, AEA={}, BVC={}, AVC={}, BER={}, AER={}) thread={}, employeeShiftAssignmentEntityDirty={}, employeeShiftAssignmentMapDirty={}",
countDirtyAllFlags.getOrDefault(getActiveThread(), 0),
countBeforeEntityAdded.getOrDefault(getActiveThread(), 0),
countAfterEntityAdded.getOrDefault(getActiveThread(), 0),
countBeforeVariableChanged.getOrDefault(getActiveThread(), 0),
countAfterVariableChanged.getOrDefault(getActiveThread(), 0),
countBeforeEntityRemoved.getOrDefault(getActiveThread(), 0),
countAfterEntityRemoved.getOrDefault(getActiveThread(), 0),
getActiveThread(),
employeeShiftAssignmentEntityDirty,
employeeShiftAssignmentMapDirty);
clearCounts();
}
private static void clearCounts() {
countDirtyAllFlags.put(getActiveThread(), 0);
countBeforeEntityAdded.put(getActiveThread(), 0);
countAfterEntityAdded.put(getActiveThread(), 0);
countBeforeVariableChanged.put(getActiveThread(), 0);
countAfterVariableChanged.put(getActiveThread(), 0);
countBeforeEntityRemoved.put(getActiveThread(), 0);
countAfterEntityRemoved.put(getActiveThread(), 0);
}
}
(note that the maps-of-booleans and maps-of-integers are effectively single booleans and integers here, because the Thread in the map lookup is currently always null due to the final useThreadFlags=false)
I confirmed that only the MoveFactory objects call the MoveFactoryHelper. Similarly, other than the VariableListener annotation above, and the flag queries/clear from MoveFactoryHelper, the only other calls to the UpdatingEmployeeVariableListener are to call UpdatingEmployeeVariableListener.setFlagsDirty() just before solving is started:
#Override
public void actionPerformed(ActionEvent e) {
UpdatingEmployeeVariableListener.setFlagsDirty();
setSolvingState(true);
Solution_ problem = solutionBusiness.getSolution();
new SolveWorker(problem).execute();
}
and just after solving is stopped:
solver.terminateEarly();
UpdatingEmployeeVariableListener.setFlagsDirty();
The template of maps-by-thread is new, but the underlying use of boolean flags has been executing for years successfully:
the flags get set dirty due to optaplanner calls to beforeVariableChanged and afterVariableChanged on the planning entities
the first MoveFactory calls the MoveFactoryHelper which calls UpdatingEmployeeVariableListener.isEmployeeShiftAssignmentMapDirty() which has a result of true. MoveFactoryHelper re-computes based on the current state and then calls to clear the dirty flags
the rest of the MoveFactory objects call MoveFactoryHelper which sees false on the is...Dirty() query, and hence can re-use its computations.
Optaplanner tests the many candidate moves, which dirties the flags again, and after choosing a move for this Step, calls the MoveFactory.createMoveList methods again early in the next step, which repeats the cycle.
Log details showing odd Optaplanner behavior
With the upgrade to 7.12, and without a moveThreadCount configuration line, the code continues to run correctly and reproducibly when I do not have the moveThreadCount xml element defined:
11:20:37.274 INFO Solving started: time spent (422), best score (0hard/-5340soft), environment mode (REPRODUCIBLE), random (JDK with seed 50).
11:20:37.280 DEBUG CH step (0), time spent (428), score (0hard/-5340soft), selected move count (1), picked move ((NullEmployee-nochange) 2018-12-25/D/0 {...}).
11:20:37.280 INFO Construction Heuristic phase (0) ended: time spent (428), best score (0hard/-5340soft), score calculation speed (1000/sec), step total (1).
11:20:37.561 DEBUG Clearing dirty flag: (AF=1, BEA=0, AEA=0, BVC=0, AVC=0, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
11:20:44.303 DEBUG LS step (0), time spent (7451), score (0hard/-4919soft), new best score (0hard/-4919soft), accepted/selected move count (1/300), picked move ([(WeekAlign-f) {...}, (WeekAlign-f) {...}]).
11:20:44.310 DEBUG Factories(10) STEP moves: 1594020
11:20:44.312 DEBUG Clearing dirty flag: (AF=0, BEA=0, AEA=0, BVC=13800, AVC=13800, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
11:20:46.609 DEBUG LS step (1), time spent (9757), score (0hard/-5266soft), best score (0hard/-4919soft), accepted/selected move count (1/24), picked move ((SlidePair) 2019-06-04/1/0... 1 shifts {...} <-slide-> {...} 3 shifts ...2019-06-07/1/0).
11:20:46.610 DEBUG Factories(10) STEP moves: 473969
11:20:46.613 DEBUG Clearing dirty flag: (AF=0, BEA=0, AEA=0, BVC=746, AVC=746, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
11:20:48.124 DEBUG LS step (2), time spent (11272), score (0hard/-5083soft), best score (0hard/-4919soft), accepted/selected move count (1/110), picked move ((CloseSlack-newEmplS) 2019-05-28/D/2(7 shifts) <-swap-> {...} 2019-05-21/D/3(7 shifts)).
11:20:48.124 DEBUG Factories(10) STEP moves: 477083
(the Factories debug log line after each step is just to show how many moves the 10 custom factories offered up to the solver in that previous step)
However, when I add the <moveThreadCount>1</moveThreadCount> line to the config file, then I see intermittent calls to rebuild the MoveFactoryHelper in the middle of Optaplanner making variable changes (see LS step 2, below):
10:46:05.413 INFO Solving started: time spent (360), best score (0hard/-5340soft), environment mode (REPRODUCIBLE), random (JDK with seed 50).
10:46:05.746 DEBUG CH step (0), time spent (693), score (0hard/-5340soft), selected move count (1), picked move ((NullEmployee-nochange) 2018-12-25/D/0 {...}).
10:46:05.746 INFO Construction Heuristic phase (0) ended: time spent (693), best score (0hard/-5340soft), score calculation speed (9/sec), step total (1).
10:46:05.949 DEBUG Clearing dirty flag: (AF=1, BEA=0, AEA=0, BVC=0, AVC=0, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
10:46:13.014 DEBUG LS step (0), time spent (7961), score (0hard/-4919soft), new best score (0hard/-4919soft), accepted/selected move count (1/300), picked move ([(WeekAlign-f) {...}, (WeekAlign-f) {...}]).
10:46:13.019 DEBUG Factories(10) STEP moves: 1594020
10:46:13.021 DEBUG Clearing dirty flag: (AF=0, BEA=0, AEA=0, BVC=13844, AVC=13844, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
10:46:14.741 DEBUG LS step (1), time spent (9688), score (0hard/-5266soft), best score (0hard/-4919soft), accepted/selected move count (1/19), picked move ((SlidePair) 2019-06-04/1/0... 1 shifts {...} <-slide-> {...} 3 shifts ...2019-06-07/1/0).
10:46:14.741 DEBUG Factories(10) STEP moves: 473969
10:46:14.743 DEBUG Clearing dirty flag: (AF=0, BEA=0, AEA=0, BVC=582, AVC=582, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
10:46:14.743 DEBUG Clearing dirty flag: (AF=0, BEA=0, AEA=0, BVC=20, AVC=20, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
10:46:16.444 DEBUG LS step (2), time spent (11391), score (0hard/-5083soft), best score (0hard/-4919soft), accepted/selected move count (1/97), picked move ((CloseSlack-newEmplS) {...} 2019-05-28/D/2(7 shifts) <-swap-> {...} 2019-05-21/D/3(7 shifts)).
10:46:16.445 DEBUG Factories(10) STEP moves: 1580032
Two comments:
First, there is some loss of reproducible execution, for example notice there are 13800 before/after variable changes originally, and 13844 now. I am presuming this is related to the "enabling" of multithreading, even though only one thread will be used.
Second, the number of variable changes and the details of the "split", where two calls are seen to clear the dirty flags (after rebuilding MoveFactoryHelper) has some variation from run to run, leading me to think it is a multithread race issue, for example:
12:16:27.712 INFO Solving started: time spent (375), best score (0hard/-5340soft), environment mode (REPRODUCIBLE), random (JDK with seed 50).
12:16:28.043 DEBUG CH step (0), time spent (706), score (0hard/-5340soft), selected move count (1), picked move ((NullEmployee-nochange) 2018-12-25/D/0 {...}).
12:16:28.043 INFO Construction Heuristic phase (0) ended: time spent (706), best score (0hard/-5340soft), score calculation speed (9/sec), step total (1).
12:16:28.288 DEBUG Clearing dirty flag: (AF=1, BEA=0, AEA=0, BVC=0, AVC=0, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
12:16:35.148 DEBUG LS step (0), time spent (7811), score (0hard/-4919soft), new best score (0hard/-4919soft), accepted/selected move count (1/300), picked move ([(WeekAlign-f) {...}, (WeekAlign-f) {...}]).
12:16:35.158 DEBUG Factories(10) STEP moves: 1594020
12:16:35.160 DEBUG Clearing dirty flag: (AF=0, BEA=0, AEA=0, BVC=13821, AVC=13821, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
12:16:35.160 DEBUG Clearing dirty flag: (AF=0, BEA=0, AEA=0, BVC=0, AVC=0, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
12:16:37.050 DEBUG LS step (1), time spent (9713), score (0hard/-5266soft), best score (0hard/-4919soft), accepted/selected move count (1/22), picked move ((SlidePair) 2019-06-04/1/0... 1 shifts {...} <-slide-> {...} 3 shifts ...2019-06-07/1/0).
12:16:37.053 DEBUG Factories(10) STEP moves: 1576812
12:16:37.054 DEBUG Clearing dirty flag: (AF=0, BEA=0, AEA=0, BVC=763, AVC=763, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
12:16:37.055 DEBUG Clearing dirty flag: (AF=0, BEA=0, AEA=0, BVC=23, AVC=23, BER=0, AER=0) thread=null, employeeShiftAssignmentEntityDirty={null=false}, employeeShiftAssignmentMapDirty={null=false}
12:16:39.414 DEBUG LS step (2), time spent (12077), score (0hard/-5083soft), best score (0hard/-4919soft), accepted/selected move count (1/98), picked move ((CloseSlack-newEmplS) {...} 2019-05-28/D/2(7 shifts) <-swap-> {...} 2019-05-21/D/3(7 shifts)).
12:16:39.414 DEBUG Factories(10) STEP moves: 1580534
Thus my two questions:
Is it right that Optaplanner does not behave the same with no moveThreadCount definition versus a definition of 1? That seems unexpected for a user.
What might I, or Optaplanner, be doing that is causing the early call to a custom MoveFactory (to generate Move lists) before all Optaplanner's variable changes are complete after the previous step, even in a single-thread configuration? I'm wondering if the "chosen move" is implemented and new createMoveList calls begin before the last of the move-scoring/testing threads from the "last" move are all paused. Even if so, I don't know why that would result in non-reproducible execution here, unless the still-running threads are making random move selections (which would seem to yield a non-reproducible execution overall).
This is occurring in both "run" and "debug" execution environments.
Thank you.
Appears resolved by Optaplanner 7.15.0. Updating to this release will fix the problem.
Related
How to "simulate" processing time consuming tasks for FreeRTOS aimed to discuss real-time system topics using Linux simulator
I'm trying to use FreeRTOS to discuss real time concepts with students, using the POSIX-Linux simulator framework. To accomplish this, I have to find a way of wasting processing time in a controlled way (simulating task in "Running" status by a predetermined period of processing time). Delays are not good because they change the task status to "Block" and, with a preemptive scheduler, it means the scheduler can give the processor to other tasks. Using Linux native time control approaches (e.g. using clock_gettime() to build the logic) are not good because I don't have the control of the exact running time of a single task, specially with preemptiveness. Regular iterations (for, while) don't have the control I need for processing time (my computer and students computers will have different processing times depending on their architectures). During my researches on FreeRTOS documentation, I found both the TaskStatus_t struct and the vTaskGetInfo() function which were supposed to help me out. And my problem is when I implement something like: // Creating the task if (xTaskCreate( app_task, task1_info.name, configMINIMAL_STACK_SIZE, (void *) &task1_info, task1_info.priority, NULL ) != pdPASS) printf("Task create error %s\n", task1_info.name); // ... starting_time_ticks = xTaskGetTickCount(); // starting time in ticks vTaskStartScheduler(); // ... static void app_task( void *pvParameters ) // The task itself { // ... for( ;; ) { // ... app_proc_ticks( pdMS_TO_TICKS( task_info.proc_time_ms ), task_info.name ); // consuming processing time... // ... } // ... } static void app_proc_ticks( TickType_t proc_time_ticks, uint8_t name[APP_MAX_MSG_SIZE]) // Consuming the number of ticks in order to attain a certain processing time { TaskHandle_t xHandle; TaskStatus_t xTaskDetails; xHandle = xTaskGetHandle( name ); configASSERT( xHandle ); vTaskGetInfo( xHandle, &xTaskDetails, pdTRUE, eInvalid ); TickType_t begin = xTaskDetails.ulRunTimeCounter; while((xTaskDetails.ulRunTimeCounter - begin) < proc_time_ticks) { vTaskGetInfo( xHandle, &xTaskDetails, pdTRUE, eInvalid ); } } For a task_info.proc_time_ms equal to 25 ms, my code shows up as the task is consuming around 250 ms worth of ticks, a error factor of 10x. The way I count this is with the following "timestamp" strategy: static TickType_t get_timestamp_ticks() { return xTaskGetTickCount() - starting_time_ticks; } As far as I can see, I'm having problems to understand and properly convert xTaskDetails.ulRunTimeCounter time unit (ticks, ms, or probably something else). Also probably some tick to ms constant I'm not aware of. Right now, to convert from "ms" to "ticks" I'm using pdMS_TO_TICKS() macro and to convert from "ticks" to "ms" I'm multiplying the number of ticks by portTICK_RATE_MS. Also, after make, I'm using taskset -c 0 ./build/posix_demo to run and ensure the use of a single processor by my executable. I'm not trying to hold on to this solution, though. If anyone could share how to do a time controlled delay with "real consumption of processing time" for tasks in FreeRTOS, I would appreciate it as well.
How does the update diff work in AzerothCore?
The worldserver source code is full of Update methods that take as input a diff integer value: How does it work? And how is this linked to the "Update time diff" from the .server info command?
To fully understand how this works, it's necessary to have a look at the main World run process. WorldRunnable::run() File: src/server/worldserver/WorldThread/WorldRunnable.cpp The method void WorldRunnable::run() is the "Main heartbeat for the World". This method runs the whole world process. Inside it, there is a while loop that runs as long as the world is supposed to keep running: void WorldRunnable::run() { uint32 realCurrTime = 0; uint32 realPrevTime = getMSTime(); ///- While we have not World::m_stopEvent, update the world while (!World::IsStopped()) { ++World::m_worldLoopCounter; realCurrTime = getMSTime(); uint32 diff = getMSTimeDiff(realPrevTime, realCurrTime); sWorld->Update( diff ); realPrevTime = realCurrTime; uint32 executionTimeDiff = getMSTimeDiff(realCurrTime, getMSTime()); devDiffTracker.Update(executionTimeDiff); avgDiffTracker.Update(executionTimeDiff > WORLD_SLEEP_CONST ? executionTimeDiff : WORLD_SLEEP_CONST); // ... some more code here } // at this point the world process is terminating // ... some more code here What this loop really does is basically: 1) calculate the elapsed time (in milliseconds) since the previous iteration, this will be the diff 2) call the sWorld->Update( diff ); function, that contains all the world process logic (see below) and passing the diff to it 3) calculate how much time it took to run sWorld->Update( diff ); and update the devDiffTracker and its average avgDiffTracker. These values will be displayed by the .server info command. World::Update(uint32 diff) File: src/server/game/World/World.cpp The World::Update(uint32 diff) function gets constantly called by the main worldserver loop process and every time it takes in input the amount diff of elapsed time since the last call. This function is responsible for constantly updating the world, this is where all the magic happens. Timers There are a set of timers (defined in World.h that are being updated within the World::Update function: /// Timers for different object refresh rates enum WorldTimers { WUPDATE_AUCTIONS, WUPDATE_WEATHERS, WUPDATE_UPTIME, WUPDATE_CORPSES, WUPDATE_EVENTS, WUPDATE_CLEANDB, WUPDATE_AUTOBROADCAST, WUPDATE_MAILBOXQUEUE, WUPDATE_PINGDB, WUPDATE_5_SECS, WUPDATE_COUNT }; For example, WUPDATE_AUTOBROADCAST is responsible for the period global messages defined in the acore_auth.autobroadcast table. Tasks The World::Update function also takes care of many timed-tasks, for example: /// Handle daily quests reset time if (m_gameTime > m_NextDailyQuestReset) ResetDailyQuests(); Calling Update(diff) functions of Managers In AzerothCore there are singleton classes called Managers (Mgr) that handle specific parts of the game. For example BattlegroundMgr handles the Battlegrounds (BGs). Those classes have their own Update(uint32 diff) functions and they are called by World::Update that passes down the diff to them, for example: sBattlegroundMgr->Update(diff); sOutdoorPvPMgr->Update(diff); sBattlefieldMgr->Update(diff); /// ... there are more! OnWorldUpdate hook Last but not least, it calls sScriptMgr->OnWorldUpdate(diff);. This is part of the AzerothCore Module System, and defines a hook that can be used by third-part modules to attach custom logic to the World::Update function.
Stanford NLP: OutOfMemoryError
I am annotating and analyzing a series of text files. The pipeline.annotate method becomes increasingly slow each time it reads a file. Eventually, I get an OutOfMemoryError. Pipeline is initialized ONCE: protected void initializeNlp() { Log.getLogger().debug("Starting Stanford NLP"); // creates a StanfordCoreNLP object, with POS tagging, lemmatization, // NER, parsing, and Properties props = new Properties(); props.put("annotators", "tokenize, ssplit, pos, lemma, ner, regexner, depparse, natlog, openie"); props.put("regexner.mapping", namedEntityPropertiesPath); pipeline = new StanfordCoreNLP(props); Log.getLogger().debug("\n\n\nStarted Stanford NLP Successfully\n\n\n"); } I then process each file using same instance of pipeline (as recommended elsewhere on SO and by Stanford). public void processFile(Path file) { try { Instant start = Instant.now(); Annotation document = new Annotation(cleanString); Log.getLogger().info("ANNOTATE"); pipeline.annotate(document); Long millis= Duration.between(start, Instant.now()).toMillis(); Log.getLogger().info("Annotation Duration in millis: "+millis); AnalyzedFile af = AnalyzedFileFactory.getAnalyzedFile(AnalyzedFileFactory.GENERIC_JOB_POST, file); processSentences(af, document); Log.getLogger().info("\n\n\nFile Processing Complete\n\n\n\n\n"); Long millis1= Duration.between(start, Instant.now()).toMillis(); Log.getLogger().info("Total Duration in millis: "+millis1); allFiles.put(file.toUri().toString(), af); } catch (Exception e) { Log.getLogger().debug(e.getMessage(), e); } } To be clear, I expect the problem is with my configuration. However, I am certain that the stall and memory issues occur at the pipeline.annotate(file) method. I dispose of all references to Stanford-NLP objects other than pipeline (e.g., CoreLabel) after processing each file. That is, I do not keep references to any Stanford objects in my code beyond the method level. Any tips or guidance would be deeply appreciated
OK, that last sentence of the question made me go double check. The answer is that I WAS keeping reference to CoreMap in one of my own classes. In other words, I was keeping in memory all the Trees, Tokens and other analyses for every sentence in my corpus. In short, keep StanfordNLP CoreMaps for a given number of sentences and then dispose. (I expect a hard core computational linguist would say there is rarely any need to keep a CoreMap once it has been analyzed, but I have to declare my neophyte status here)
CRM PlugIn Pass Variable Flag to New Execution Pipeline
I have records that have an index attribute to maintain their position in relation to each other. I have a plugin that performs a renumbering operation on these records when the index is changed or new one created. There are specific rules that apply to items that are at the first and last position in the list. If a new (or existing changed) item is inserted into the middle (not technically the middle...just somewhere between start and end) of the list a renumbering kicks off to make room for the record. This renumbering process fires in a new execution pipeline...We are updating record D. When I tell record E to change (to make room for D) that of course fires the plugin on update message. This renumbering is fine until we reach the end of the list where the plugin then gets into a loop with the first business rule that maintains the first and last record differently. So I am trying to think of ways to pass a flag to the execution context spawned by the renumbering process so the recursion skips the boundary edge business rules if IsRenumbering == true. My thoughts / ideas: I have thought of using the Depth check > 1 but that isn't a reliable value as I can't explicitly turn it on or off....it may happen to work but that is not engineering a solid solution that is hoping nothing goes bump. Further a colleague far more knowledgeable than I said that when a workflow calls a plugin the depth value is off and can't be trusted. All my variables are scoped at the execute level so as to avoid variable pollution at the class level....However if I had a dictionary object, tuple, something at the class level and one value would be the thread id and the other the flag value then perhaps my subsequent execution context could check if the same owning thread id had any values entered. Any thoughts or other ideas on how to pass context information to a new pipeline would be greatly appreciated. Per Nicknow sugestion I tried sharedvariables but they seem to be going out of scope...: First time firing post op: if (base.Stage == EXrmPluginStepStage.PostOperation) { ...snip... foreach (var item in RenumberSet) { Context.ParentContext.SharedVariables[recordrenumbering] = "googly"; Entity renumrec = new Entity("abcd") { Id = item.Id }; #region We either add or subtract indexes based upon sortdir ...snip... renumrec["abc_indexfield"] = TmpIdx + 1; break; .....snip..... #endregion OrganizationService.Update(renumrec); } } Now we come into Pre-Op of the recursion process kicked off by the above post-op OrganizationService.Update(renumrec); and it seems based upon this check the sharedvariable didn't carry over...??? if (!Context.SharedVariables.Contains(recordrenumbering)) { //Trace.Trace("Null Set"); //Context.SharedVariables[recordrenumbering] = IsRenumbering; Context.SharedVariables[recordrenumbering] = "Null Set"; } throw invalidpluginexception reveals: Sanity Checks: Depth : 2 Entity: ... Message: Update Stage: PreOperation [20] User: 065507fe-86df-e311-95fe-00155d050605 Initiating User: 065507fe-86df-e311-95fe-00155d050605 ContextEntityName: .... ContextParentEntityName: .... .... IsRenumbering: Null Set
What are you looking for is IExecutionContext.SharedVariables. Whatever you add here is available throughout the entire transaction. Since you'll have child pipelines you'll want to look at the ParentContext for the value. This can all get a little tricky, so be sure to do a lot of testing - I've run into many issues with SharedVariables and looping operations in Dynamics CRM. Here is some sample (very untested) code to get you started. public static bool GetIsRenumbering(IPluginExecutionContext pluginContext) { var keyName = "IsRenumbering"; var ctx = pluginContext; while (ctx != null) { if (ctx.SharedVariables.Contains(keyName)) { return (bool)ctx.SharedVariables[keyName]; } else ctx = ctx.ParentContext; } return false; } public static void SetIsRenumbering(IPluginExecutionContext pluginContext) { var keyName = "IsRenumbering"; var ctx = pluginContext; ctx.SharedVariables.Add(keyName, true); }
A very simple solution: add a bit field to the entity called "DisableIndexRecalculation." When your first plugin runs, make sure to set that field to true for all of your updates. In the same plugin, check to see if "DisableIndexRecalculation" is set to true: if so, set it to null (by removing it from the TargetEntity entirely) and stop executing the plugin. If it is null, do your index recalculation. Because you are immediately removing the field from the TargetEntity if it is true the value will never be persisted to the database so there will be no performance penalty.
Entry element for mm:ss with MonoTouch.Dialog?
I am giving a try to MonoTouch and MonoTouch.Dialog and was wondering what would be the best way to create a Time entry element for minutes and seconds only. The TimeElement only seems to support hours and minutes. I am very new to the whole framework, so I am wondering if there a way to create a text entry element with sort of a "##:##" mask and use a numeric keypad to populate the minutes and seconds? A cooler option would be to use a section that would take the user to a view with two "Picker Views" (rotating wheels) for minutes and seconds, but I am not there yet. Thanks in advance
MonoTouch.Dialog's source code is available on Github (https://github.com/migueldeicaza/MonoTouch.Dialog/tree/master/MonoTouch.Dialog). There, have a look at the implementation of the DateElement (https://github.com/migueldeicaza/MonoTouch.Dialog/blob/master/MonoTouch.Dialog/Elements.cs#L1827) To get you a TimeElementMinutesSeconds, all you have to do is (untested) abuse the count down mode. It will offer hours and minutes, but you can just interpret them as minutes and seconds: public class TimeElementMinutesSeconds : DateTimeElement { public TimeElementMinutesSeconds (string caption, DateTime date) : base (caption, date) { } public override string FormatDate (DateTime dt) { DateTime dtLocal = GetDateWithKind (dt).ToLocalTime (); return string.Format("{0:##}:{1:##}"dtLocal.Hours, dtLocal.Minutes); } public override UIDatePicker CreatePicker () { var picker = base.CreatePicker (); picker.Mode = UIDatePickerMode.CountDownTimer; return picker; } } If that doesn't get you near enough, you can create your own picker and return that instead.