Skip to content

Commit 423a252

Browse files
authored
Version 1.20260803.0.
Performance work - Logger atomics - LLVM JIT host CPU targeting - SED-ML error/warning restore - ODE solver computeRates() caching - CellML runtime: WASM table dispatch - KINSOL solver object caching Benchmarking & testing - Simulation benchmarks - Test coverage
2 parents 7e53a99 + 6f6b8a0 commit 423a252

38 files changed

Lines changed: 14367 additions & 282 deletions

‎.codespellexclude‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
afterAll(() => {
1+
<!-- BG CVS MVP + Finbar's ANS + Peter's Blood volume Ctl model -->
2+
<!-- controlling and modulating the resistance by ANS
23
IssuePtrs mErrors;
3-
return !mErrors.empty();
4-
return mErrors.size();
54
return mErrors;
6-
mErrors.push_back(issue);
7-
mErrors.clear();
85
if (pIndex >= mErrors.size()) {
96
return mErrors[pIndex];
107
mErrors.push_back(std::move(issue));
8+
mErrors.clear();
9+
mTasksErrors = mErrors;
10+
mErrors.clear();
11+
// Note: the clearing of mIssues, mErrors, and mWarnings could be done using removeAllIssues(), but this would
12+
mErrors = mTasksErrors;
13+
mErrorCount.store(mErrors.size(), std::memory_order_release);
14+
if ((document->getNumErrors() == 0)

‎VERSION.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.20260723.2
1+
1.20260803.0

‎newversion‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ old_patch_version=$(echo $old_version | cut -d. -f3)
1212

1313
# Determine the new version based on the current version and the current date.
1414

15-
now=$(date +%Y%m%d)
15+
now=$(TZ=Pacific/Auckland date +%Y%m%d)
1616

1717
new_major_version=$old_major_version
1818
new_minor_version=$now

‎newversion.bat‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ FOR /F "tokens=1-3 delims=." %%A IN ("!OldVersion!") DO (
1616

1717
REM Determine the new version based on the current version and the current date.
1818

19-
FOR /F "tokens=2 delims==" %%I IN ('wmic os get localdatetime /value') DO SET Now=%%I
19+
FOR /F "usebackq delims=" %%I IN (`powershell -NoProfile -Command "[System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([System.DateTime]::UtcNow, 'New Zealand Standard Time').ToString('yyyyMMdd')"`) DO SET Now=%%I
2020

2121
SET NewMajorVersion=!OldMajorVersion!
2222
SET NewMinorVersion=!Now:~0,8!

‎src/logger/logger.cpp‎

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,24 @@ namespace libOpenCOR {
2121

2222
bool Logger::Impl::hasIssues() const
2323
{
24-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
25-
26-
return !mIssues.empty();
24+
return mIssueCount.load(std::memory_order_acquire) != 0;
2725
}
2826

2927
size_t Logger::Impl::issueCount() const
3028
{
31-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
32-
33-
return mIssues.size();
29+
return mIssueCount.load(std::memory_order_acquire);
3430
}
3531

3632
IssuePtrs Logger::Impl::issues() const
3733
{
38-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
34+
const std::scoped_lock<std::mutex> lock(mMutex);
3935

4036
return mIssues;
4137
}
4238

4339
IssuePtr Logger::Impl::issue(size_t pIndex) const
4440
{
45-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
41+
const std::scoped_lock<std::mutex> lock(mMutex);
4642

4743
if (pIndex >= mIssues.size()) {
4844
return nullptr;
@@ -53,28 +49,24 @@ IssuePtr Logger::Impl::issue(size_t pIndex) const
5349

5450
bool Logger::Impl::hasErrors() const
5551
{
56-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
57-
58-
return !mErrors.empty();
52+
return mErrorCount.load(std::memory_order_acquire) != 0;
5953
}
6054

6155
size_t Logger::Impl::errorCount() const
6256
{
63-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
64-
65-
return mErrors.size();
57+
return mErrorCount.load(std::memory_order_acquire);
6658
}
6759

6860
IssuePtrs Logger::Impl::errors() const
6961
{
70-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
62+
const std::scoped_lock<std::mutex> lock(mMutex);
7163

7264
return mErrors;
7365
}
7466

7567
IssuePtr Logger::Impl::error(size_t pIndex) const
7668
{
77-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
69+
const std::scoped_lock<std::mutex> lock(mMutex);
7870

7971
if (pIndex >= mErrors.size()) {
8072
return nullptr;
@@ -85,28 +77,24 @@ IssuePtr Logger::Impl::error(size_t pIndex) const
8577

8678
bool Logger::Impl::hasWarnings() const
8779
{
88-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
89-
90-
return !mWarnings.empty();
80+
return mWarningCount.load(std::memory_order_acquire) != 0;
9181
}
9282

9383
size_t Logger::Impl::warningCount() const
9484
{
95-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
96-
97-
return mWarnings.size();
85+
return mWarningCount.load(std::memory_order_acquire);
9886
}
9987

10088
IssuePtrs Logger::Impl::warnings() const
10189
{
102-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
90+
const std::scoped_lock<std::mutex> lock(mMutex);
10391

10492
return mWarnings;
10593
}
10694

10795
IssuePtr Logger::Impl::warning(size_t pIndex) const
10896
{
109-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
97+
const std::scoped_lock<std::mutex> lock(mMutex);
11098

11199
if (pIndex >= mWarnings.size()) {
112100
return nullptr;
@@ -139,15 +127,21 @@ void Logger::Impl::addIssues(const libcellml::LoggerPtr &pLogger, const std::str
139127

140128
void Logger::Impl::addIssue(Issue::Type pType, const std::string &pDescription, const std::string &pContext)
141129
{
142-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
130+
const std::scoped_lock<std::mutex> lock(mMutex);
143131

144132
auto issue {IssuePtr {new Issue {pType, pDescription, pContext}}};
145133
mIssues.push_back(issue);
146134

135+
mIssueCount.fetch_add(1, std::memory_order_release);
136+
147137
if (pType == Issue::Type::ERROR) {
148138
mErrors.push_back(std::move(issue));
139+
140+
mErrorCount.fetch_add(1, std::memory_order_release);
149141
} else {
150142
mWarnings.push_back(std::move(issue));
143+
144+
mWarningCount.fetch_add(1, std::memory_order_release);
151145
}
152146
}
153147

@@ -163,12 +157,15 @@ void Logger::Impl::addWarning(const std::string &pDescription)
163157

164158
void Logger::Impl::removeAllIssues()
165159
{
166-
const std::scoped_lock<std::recursive_mutex> lock(mMutex);
160+
const std::scoped_lock<std::mutex> lock(mMutex);
167161

168162
mIssues.clear();
169-
170163
mErrors.clear();
171164
mWarnings.clear();
165+
166+
mIssueCount.store(0, std::memory_order_release);
167+
mErrorCount.store(0, std::memory_order_release);
168+
mWarningCount.store(0, std::memory_order_release);
172169
}
173170

174171
Logger::Logger(std::unique_ptr<Impl> pPimpl)

‎src/logger/logger_p.h‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,31 @@ limitations under the License.
2222
#include "libopencor/issue.h"
2323
#include "libopencor/logger.h"
2424

25+
#include <atomic>
2526
#include <mutex>
2627

2728
namespace libOpenCOR {
2829

2930
class Logger::Impl
3031
{
3132
public:
32-
mutable std::recursive_mutex mMutex;
33+
mutable std::mutex mMutex;
3334

3435
IssuePtrs mIssues;
35-
3636
IssuePtrs mErrors;
3737
IssuePtrs mWarnings;
3838

39+
// Note: we use atomic variables for the issue counts so that they can be read without locking the mutex, which is
40+
// otherwise needed to access the issue vectors themselves. This is because the issue counts can be read
41+
// frequently during a simulation and we want to avoid the overhead of locking the mutex. To ensure that a
42+
// reader never sees an updated count before the corresponding issue vectors have been updated, the counts are
43+
// written with std::memory_order_release and read with std::memory_order_acquire. The vectors themselves are
44+
// still protected by the mutex.
45+
46+
std::atomic<size_t> mIssueCount {0};
47+
std::atomic<size_t> mErrorCount {0};
48+
std::atomic<size_t> mWarningCount {0};
49+
3950
virtual ~Impl() = default;
4051

4152
bool hasIssues() const;
@@ -57,7 +68,6 @@ class Logger::Impl
5768
void addIssues(const libcellml::LoggerPtr &pLogger, const std::string &pContext);
5869

5970
void addIssue(Issue::Type pType, const std::string &pDescription, const std::string &pContext = "");
60-
6171
void addError(const std::string &pDescription);
6272
void addWarning(const std::string &pDescription);
6373

‎src/misc/compiler.cpp‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,10 +632,14 @@ extern double atanh(double);
632632

633633
return true;
634634
#else
635-
// Create an ORC-based JIT with aggressive code generation and keep track of it.
635+
// Create an ORC-based JIT with a target machine builder for the host system.
636+
// Note: we set the CPU to the host CPU name and the optimisation level to aggressive. This is because the default
637+
// CPU is generic and the default optimisation level is none, which can lead to suboptimal performance for the
638+
// generated code.
636639

637640
auto jitTargetMachineBuilder {llvm::orc::JITTargetMachineBuilder(llvm::Triple(llvm::sys::getProcessTriple()))};
638641

642+
jitTargetMachineBuilder.setCPU(llvm::sys::getHostCPUName().str());
639643
jitTargetMachineBuilder.setCodeGenOptLevel(llvm::CodeGenOptLevel::Aggressive);
640644

641645
auto lljit {llvm::orc::LLJITBuilder().setJITTargetMachineBuilder(std::move(jitTargetMachineBuilder)).create()};

‎src/sed/sedinstance.cpp‎

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,16 @@ SedInstance::Impl::Impl(const SedDocumentPtr &pDocument)
7878
}
7979
}
8080
}
81-
82-
// Keep track of the issues of all the tasks.
83-
84-
mTasksIssues = mIssues;
8581
} else {
8682
addError("The simulation experiment description does not contain any tasks to run.");
8783
}
84+
85+
// Keep track of the issues so that they can be restored should the instance be run.
86+
// Note: this includes the issues generated above (e.g., when there are no tasks to run).
87+
88+
mTasksIssues = mIssues;
89+
mTasksErrors = mErrors;
90+
mTasksWarnings = mWarnings;
8891
}
8992

9093
SedInstance::Status SedInstance::Impl::status() const
@@ -102,11 +105,28 @@ SedInstance::Status SedInstance::Impl::status() const
102105

103106
double SedInstance::Impl::run()
104107
{
105-
// Reset ourselves.
106-
107-
removeAllIssues();
108-
109-
mIssues = mTasksIssues;
108+
// Reset ourselves by restoring the issues of all the tasks.
109+
// Note: the clearing of mIssues, mErrors, and mWarnings could be done using removeAllIssues(), but this would
110+
// result in transiently-empty issues, which could be seen by a reader. So, instead, we just clear and restore
111+
// the issues in one go.
112+
// Note: the issue counts are published with std::memory_order_release so that readers using
113+
// std::memory_order_acquire never see an updated count before the corresponding issue vectors are visible.
114+
115+
{
116+
const std::scoped_lock<std::mutex> lock(mMutex);
117+
118+
mIssues.clear();
119+
mErrors.clear();
120+
mWarnings.clear();
121+
122+
mIssues = mTasksIssues;
123+
mErrors = mTasksErrors;
124+
mWarnings = mTasksWarnings;
125+
126+
mIssueCount.store(mIssues.size(), std::memory_order_release);
127+
mErrorCount.store(mErrors.size(), std::memory_order_release);
128+
mWarningCount.store(mWarnings.size(), std::memory_order_release);
129+
}
110130

111131
// Reset our control flags and make sure that they are passed to each task so that they can be used by them.
112132

‎src/sed/sedinstance_p.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ class SedInstance::Impl: public Logger::Impl
3131
{
3232
public:
3333
SedInstanceTaskPtrs mTasks;
34+
3435
IssuePtrs mTasksIssues;
36+
IssuePtrs mTasksErrors;
37+
IssuePtrs mTasksWarnings;
3538

3639
mutable std::atomic<bool> mRunning {false};
3740
mutable std::mutex mRunMutex;

‎src/sed/sedinstancetask.cpp‎

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -257,39 +257,20 @@ void SedInstanceTask::Impl::initialise()
257257
if (mSedUniformTimeCourse != nullptr) {
258258
mVoi = mSedUniformTimeCourse->pimpl()->mInitialTime;
259259

260-
#ifdef __EMSCRIPTEN__
261-
mRuntime->initialiseArraysForDifferentialModel(mStates, mRates, mConstants, mComputedConstants, mAlgebraicVariables);
262-
#else
263260
mRuntime->initialiseArraysForDifferentialModel()(mStates, mRates, mConstants, mComputedConstants, mAlgebraicVariables);
264-
#endif
265261
} else {
266-
#ifdef __EMSCRIPTEN__
267-
mRuntime->initialiseArraysForAlgebraicModel(mConstants, mComputedConstants, mAlgebraicVariables);
268-
#else
269262
mRuntime->initialiseArraysForAlgebraicModel()(mConstants, mComputedConstants, mAlgebraicVariables);
270-
#endif
271263
}
272264

273265
applyChanges();
274266

275267
if (mSedUniformTimeCourse != nullptr) {
276-
#ifdef __EMSCRIPTEN__
277-
mRuntime->computeComputedConstantsForDifferentialModel(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraicVariables);
278-
mRuntime->computeRates(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraicVariables);
279-
mRuntime->computeVariablesForDifferentialModel(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraicVariables);
280-
#else
281268
mRuntime->computeComputedConstantsForDifferentialModel()(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraicVariables);
282269
mRuntime->computeRates()(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraicVariables);
283270
mRuntime->computeVariablesForDifferentialModel()(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraicVariables);
284-
#endif
285271
} else {
286-
#ifdef __EMSCRIPTEN__
287-
mRuntime->computeComputedConstantsForAlgebraicModel(mConstants, mComputedConstants, mAlgebraicVariables);
288-
mRuntime->computeVariablesForAlgebraicModel(mConstants, mComputedConstants, mAlgebraicVariables);
289-
#else
290272
mRuntime->computeComputedConstantsForAlgebraicModel()(mConstants, mComputedConstants, mAlgebraicVariables);
291273
mRuntime->computeVariablesForAlgebraicModel()(mConstants, mComputedConstants, mAlgebraicVariables);
292-
#endif
293274
}
294275

295276
// Make sure that the NLA solver, should it have been used, didn't report any issues.
@@ -355,9 +336,7 @@ void SedInstanceTask::Impl::run(double pVoiStart, double pVoiEnd, double pVoiInt
355336
auto *odeSolverPimpl {mOdeSolver->pimpl()};
356337
size_t voiCounter {0};
357338

358-
#ifndef __EMSCRIPTEN__
359339
const auto computeVariablesForDifferentialModel = mRuntime->computeVariablesForDifferentialModel();
360-
#endif
361340

362341
while (!fuzzyCompare(mVoi, pVoiEnd)) {
363342
// Check whether a pause or stop has been requested.
@@ -396,11 +375,7 @@ void SedInstanceTask::Impl::run(double pVoiStart, double pVoiEnd, double pVoiInt
396375
return;
397376
}
398377

399-
#ifdef __EMSCRIPTEN__
400-
mRuntime->computeVariablesForDifferentialModel(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraicVariables);
401-
#else
402378
computeVariablesForDifferentialModel(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraicVariables);
403-
#endif
404379

405380
//---GRY--- WE NEED TO CHECK FOR POSSIBLE NLA ISSUES, BUT FOR CODE COVERAGE WE NEED A MODEL THAT WOULD TRIGGER
406381
// NLA ISSUES HERE, WHICH WE DON'T HAVE YET HENCE WE DISABLE THE FOLLOWING CODE WHEN DOING CODE

0 commit comments

Comments
 (0)