12 this->master = master;
22 mutex->enter(5000, __FUNCTION__);
33 if (!mutex || !mutex->enter(5000, __FUNCTION__))
38 usage = header->usage;
45 if (!mutex || !mutex->enter(5000, __FUNCTION__))
53bool TemporalMemory::create(uint32 slotCount, uint16 binCount, uint32 minBlockSize, uint32 maxBlockSize, uint64 initSize, uint64 maxSize, uint32 growSteps) {
54 serial = master->incrementDynamicShmemSerial();
55 port = master->getID();
62 std::vector<BinHeaderStruct> binHeaders = calcBinHeaders(binCount, minBlockSize, maxBlockSize, binDataSize);
67 std::vector<BinHeaderStruct>::iterator i = binHeaders.begin(), e = binHeaders.end();
69 newInitSize += i->size;
74 if (!mutex->enter(5000, __FUNCTION__))
77 memorySize = newInitSize;
85 header->size = memorySize;
86 header->maxSize = maxSize;
87 if (memorySize >= maxSize)
89 else if (maxSize - memorySize < 10000000)
90 header->growStep = maxSize - memorySize;
92 header->growStep = (maxSize - memorySize) / (growSteps ? growSteps : 8);
95 header->slotCount = slotCount;
96 header->slotsInUse = 0;
97 header->checkTimeInterval = 1000000;
98 header->binCount = binCount;
99 header->bitFieldSize = bitFieldSize;
102 master->setDynamicShmemSize(memorySize);
109 memset((
char*)slotEntries, 0, (
size_t)(slotCount *
sizeof(
SlotEntryStruct)));
111 uint64 binHeaderUsage = 0;
115 i = binHeaders.begin(), e = binHeaders.end();
121 binHeaderUsage += binHeader->
usage;
132std::vector<BinHeaderStruct> TemporalMemory::calcBinHeaders(uint32 binCount, uint32 minBlockSize, uint32 maxBlockSize, uint64 binDataSize) {
135 std::vector<BinHeaderStruct> binHeaders(binCount);
138 if ((binCount < 1) || (minBlockSize > maxBlockSize) || (binDataSize < (binCount * minBlockSize)))
142 binHeaders[0].size = binDataSize;
143 binHeaders[0].blockSize = minBlockSize;
144 binHeaders[0].blocksInUse = 0;
145 binHeaders[0].blockCount = (uint32)((binDataSize -
sizeof(
BinHeaderStruct)) / minBlockSize);
149 else if (binCount == 2) {
150 binHeaders[0].size = binDataSize / 2;
151 binHeaders[0].blockSize = minBlockSize;
152 binHeaders[0].blocksInUse = 0;
153 binHeaders[0].blockCount = (uint32)((binHeaders[0].size -
sizeof(BinHeaderStruct)) / minBlockSize);
156 binHeaders[1].size = binDataSize / 2;
157 binHeaders[1].blockSize = maxBlockSize;
158 binHeaders[1].blocksInUse = 0;
159 binHeaders[1].blockCount = (uint32)((binHeaders[1].size -
sizeof(BinHeaderStruct)) / maxBlockSize);
164 binHeaders[0].size = (uint64)(binDataSize * 0.25);
165 uint64 memoryLeft = binDataSize - binHeaders[0].size;
166 long double allocation = 0.5 / ((double)binCount - 2);
167 for (n = 1; n < binCount-1; n++) {
168 binHeaders[n].size = (uint64)(binDataSize * allocation);
169 memoryLeft -= binHeaders[n].size;
171 binHeaders[binCount - 1].size = memoryLeft;
174 memoryLeft = binDataSize;
175 uint32 blockSize = minBlockSize;
176 uint32 blockSizeStep = (maxBlockSize - minBlockSize) / (binCount-1);
177 for (n = 0; n < binCount; n++) {
178 binHeaders[n].blockSize = blockSize;
179 binHeaders[n].blocksInUse = 0;
180 binHeaders[n].blockCount = (uint32)((binDataSize -
sizeof(BinHeaderStruct)) / blockSize);
183 while (
sizeof(BinHeaderStruct) + binHeaders[n].bitFieldSize + (binHeaders[n].blockCount * blockSize) > binHeaders[n].size) {
184 binHeaders[n].blockCount -= 1;
187 binHeaders[n].usage =
sizeof(BinHeaderStruct) + binHeaders[n].bitFieldSize;
189 binHeaders[n].size = binHeaders[n].usage + (binHeaders[n].blockCount * blockSize);
190 memoryLeft -= binHeaders[n].size;
191 blockSize += blockSizeStep;
195 if (memoryLeft > minBlockSize) {
196 uint32 growBlocks = (uint32)(memoryLeft / minBlockSize);
197 binHeaders[0].blockCount += growBlocks;
199 while (
sizeof(BinHeaderStruct) + binHeaders[0].bitFieldSize + (binHeaders[0].blockCount * binHeaders[0].blockSize) > binHeaders[0].size) {
200 binHeaders[0].blockCount -= 1;
210 serial = master->getDynamicShmemSerial();
211 uint64 size = master->getDynamicShmemSize();
212 this->port = master->getID();
214 bool createdMutex =
false;
218 if (!mutex->enter(5000, __FUNCTION__))
251BinHeaderStruct* TemporalMemory::getBestBin(uint32 size, uint32& bin, uint32 &blocksNeeded) {
254 for (bin = 0; bin < ((uint32)header->binCount-1); bin++) {
256 blocksNeeded = (uint32)ceil((
double)size / binHeader->
blockSize);
258 if (blocksNeeded < 5)
262 blocksNeeded = (uint32)ceil((
double)size / binHeader->
blockSize);
291 uint64 eol = msg->
getEOL();
293 LogPrint(0,
LOG_MEMORY, 0,
"Could not add message to TemporalMemory, expired already");
297 if (!mutex || !mutex->enter(5000, __FUNCTION__) || !msg)
303 uint32 msgSize = msg->
getSize();
308 if (!resizeSlots()) {
309 LogPrint(0,
LOG_MEMORY, 0,
"Could not add message to TemporalMemory, slot resize error");
313 LogPrint(0,
LOG_MEMORY, 0,
"Could not add message to TemporalMemory, slot post resize error");
322 LogPrint(0,
LOG_MEMORY, 0,
"Could not add message to TemporalMemory, bin search error");
332 if (!resizeBin(bin, binHeader, msg->
getSize())) {
333 LogPrint(0,
LOG_MEMORY, 0,
"Could not add message to TemporalMemory, bin resize error");
337 binHeader = getBestBin(msg->
getSize(), bin, blocksNeeded);
339 LogPrint(0,
LOG_MEMORY, 0,
"Could not add message to TemporalMemory, bin post resize search error");
343 LogPrint(0,
LOG_MEMORY, 0,
"Could not add message to TemporalMemory, bin post resize error");
349 uint64 localOffset = startBlock * binHeader->
blockSize;
353 slotEntry->
bin = bin;
354 slotEntry->
eol = eol;
355 slotEntry->
offset = localOffset;
357 slotEntry->
size = msgSize;
364 header->slotsInUse++;
365 header->usage += slotEntry->
usage;
370 if (header->usage > 1000000000)
383 if (!mutex || !mutex->enter(5000, __FUNCTION__))
389 char* block = getMemoryBlock(
id);
400char* TemporalMemory::getMemoryBlock(uint64
id) {
405 if (slot > header->slotCount)
412 || !slotEntry->
size ) {
417 BinHeaderStruct* binHeader = (BinHeaderStruct*)(((
char*)header) +
sizeof(TemporalMemoryStruct) + header->bitFieldSize + (header->slotCount *
sizeof(SlotEntryStruct)));
418 for (uint32 n = 0; n < slotEntry->
bin; n++)
419 binHeader = (BinHeaderStruct*)(((
char*)binHeader) + binHeader->size);
421 return (((
char*)binHeader) +
sizeof(BinHeaderStruct) + binHeader->bitFieldSize + slotEntry->
offset);
424bool TemporalMemory::checkSlots(uint64 now) {
426 if ((header->lastCheckTime && (now > header->lastCheckTime + header->checkTimeInterval)) || !header->slotsInUse)
433 LogPrint(0,
LOG_MEMORY, 0,
"TemporalMemory::checkSlots inconsistency, no slots found, but header claims %u slots in use...", header->slotsInUse);
437 SlotEntryStruct* slotEntry = (SlotEntryStruct*)((
char*)header +
sizeof(TemporalMemoryStruct) + header->bitFieldSize);
439 BinHeaderStruct* binHeader;
440 for (uint32 n = 0; n <= maxSlot; n++) {
441 if (slotEntry->size && (now >= slotEntry->eol)) {
443 binHeader = (BinHeaderStruct*)(((
char*)header) +
sizeof(TemporalMemoryStruct) + header->bitFieldSize + (header->slotCount *
sizeof(SlotEntryStruct)));
444 for (uint32 i = 0; i < slotEntry->bin; i++)
445 binHeader = (BinHeaderStruct*)(((
char*)binHeader) + binHeader->size);
446 utils::SetBitN(slotEntry->startBlock, slotEntry->blockUsage,
BITFREE, ((
char*)binHeader +
sizeof(BinHeaderStruct)), binHeader->bitFieldSize);
447 binHeader->usage -= slotEntry->usage;
448 binHeader->blocksInUse -= slotEntry->blockUsage;
449 header->usage -= slotEntry->usage;
450 header->slotsInUse--;
454 utils::SetBit(n,
BITFREE, ((
char*)header +
sizeof(TemporalMemoryStruct)), header->bitFieldSize);
462bool TemporalMemory::resizeSlots() {
464 uint32 slotGrowth = header->slotCount * 4;
465 std::vector<BinHeaderStruct> binHeaderSizes = calcBinHeadersGrowth();
466 return resizeMemory(slotGrowth, binHeaderSizes);
469bool TemporalMemory::resizeBin(uint32 bin,
BinHeaderStruct* binHeader, uint32 msgSize) {
470 uint32 slotGrowth = 0;
471 if ((
double)header->slotsInUse / header->slotCount > 0.8)
472 slotGrowth = header->slotCount * 4;
473 else if ((
double)header->slotsInUse / header->slotCount > 0.5)
474 slotGrowth = header->slotCount * 2;
475 std::vector<BinHeaderStruct> binHeaderSizes = calcBinHeadersGrowth(bin, msgSize);
476 return resizeMemory(slotGrowth, binHeaderSizes);
479std::vector<BinHeaderStruct> TemporalMemory::calcBinHeadersGrowth(uint32 bin, uint32 msgSize) {
481 std::vector<BinHeaderStruct> binHeaders(header->binCount);
483 BinHeaderStruct* binHeader = (BinHeaderStruct*)(((
char*)header) +
sizeof(TemporalMemoryStruct) + header->bitFieldSize + (header->slotCount *
sizeof(SlotEntryStruct)));
484 for (uint32 n = 0; n < header->binCount; n++) {
485 binHeaders[n] = *binHeader;
486 if ( ((
double)binHeader->usage / binHeader->size > 0.8)
487 || (msgSize && (n == bin)) ) {
488 binHeaders[n].blockCount =
clmax(binHeaders[n].blockCount * 4, binHeaders[n].blockCount + (uint32)(20.0*((
double)msgSize/binHeader->blockSize)));
490 binHeaders[n].size =
sizeof(BinHeaderStruct) + binHeaders[n].bitFieldSize + (binHeaders[n].blockCount * binHeaders[n].blockSize);
493 binHeader = (BinHeaderStruct*)(((
char*)binHeader) + binHeader->size);
498bool TemporalMemory::resizeMemory(uint32 slotGrowth, std::vector<BinHeaderStruct>& binHeaderSizes) {
500 uint32 slotCount = header->slotCount + slotGrowth;
502 uint64 newTotalSize =
sizeof(TemporalMemoryStruct) + bitFieldSize + (slotCount *
sizeof(SlotEntryStruct));
504 for (uint32 n = 0; n < header->binCount; n++) {
505 newTotalSize += binHeaderSizes[n].size;
508 if (newTotalSize > header->maxSize) {
509 LogPrint(0,
LOG_MEMORY, 0,
"Temporal Memory hit maximum size %s when asked to grow by: %s to: %s total",
517 serial = master->incrementDynamicShmemSerial();
521 LogPrint(0,
LOG_MEMORY, 0,
"Temporal Memory couldn't allocate memory to grow by: %s to: %s total as requested",
529 memset((
char*)newHeader +
sizeof(TemporalMemoryStruct), 255, bitFieldSize);
531 memcpy((
char*)newHeader, header,
sizeof(TemporalMemoryStruct) + header->bitFieldSize);
532 newHeader->size = newTotalSize;
534 memset((
char*)newHeader +
sizeof(TemporalMemoryStruct) + bitFieldSize, 0, (
size_t)(slotCount *
sizeof(SlotEntryStruct)));
536 memcpy((
char*)newHeader +
sizeof(TemporalMemoryStruct) + bitFieldSize,
537 (
char*)header +
sizeof(TemporalMemoryStruct) + header->bitFieldSize, (
size_t)(header->slotCount *
sizeof(SlotEntryStruct)));
539 newHeader->slotCount = slotCount;
540 newHeader->bitFieldSize = bitFieldSize;
542 uint64 binGrowth = 0;
543 BinHeaderStruct* binHeader = (BinHeaderStruct*)(((
char*)header) +
sizeof(TemporalMemoryStruct) + header->bitFieldSize + (header->slotCount *
sizeof(SlotEntryStruct)));
544 BinHeaderStruct* newBinHeader = (BinHeaderStruct*)(((
char*)newHeader) +
sizeof(TemporalMemoryStruct) + newHeader->bitFieldSize + (newHeader->slotCount *
sizeof(SlotEntryStruct)));
545 for (uint32 n = 0; n < header->binCount; n++) {
546 if (binHeaderSizes[n].size == binHeader->size) {
548 memcpy((
char*)newBinHeader, binHeader, (
size_t)binHeader->size);
553 memset((
char*)newBinHeader +
sizeof(BinHeaderStruct), 255, binHeaderSizes[n].bitFieldSize);
555 memcpy((
char*)newBinHeader, binHeader,
sizeof(BinHeaderStruct) + binHeader->bitFieldSize);
556 newBinHeader->bitFieldSize = binHeaderSizes[n].bitFieldSize;
557 newBinHeader->blockCount = binHeaderSizes[n].blockCount;
558 newBinHeader->size = binHeaderSizes[n].size;
560 memcpy((
char*)newBinHeader +
sizeof(BinHeaderStruct) + newBinHeader->bitFieldSize,
561 (
char*)binHeader +
sizeof(BinHeaderStruct) + binHeader->bitFieldSize,
562 (
size_t)(binHeader->size - (
sizeof(BinHeaderStruct) + binHeader->bitFieldSize)));
564 binHeader = (BinHeaderStruct*)(((
char*)binHeader) + binHeader->size);
565 newBinHeader = (BinHeaderStruct*)(((
char*)newBinHeader) + newBinHeader->size);
568 if (slotGrowth && binGrowth)
569 LogPrint(0,
LOG_MEMORY, 0,
"Temporal Memory slots grown by: %u and %llu bins storage by %s to: %s total as requested",
576 LogPrint(0,
LOG_MEMORY, 0,
"Temporal Memory slots grown by: %u (%s to: %s total) as requested",
582 LogPrint(0,
LOG_MEMORY, 0,
"Temporal Memory %llu storage bins grown by: %s to: %s total as requested",
591 memorySize = newTotalSize;
592 master->setDynamicShmemSize(memorySize);
743 std::string str =
utils::StringFormat(
"TemporalMemory \tsize: %s \tusage: %s \tslots: %u \tinuse: %u\n",
751 for (uint32 n = 0; n < header->binCount; n++) {
766 uint32 slotCount = 1000;
768 uint32 minBlockSize = 1024;
769 uint32 maxBlockSize = 64*1024;
770 uint64 initSize = 50000000;
771 uint64 maxSize = 500000000;
772 uint64 msgTTL = 5000000;
777 uint32 outerCircle = 40;
778 uint32 innerCircle = 30;
782 if (!masterMemory.
create(12000)) {
790 if (!temporalMemory.
create(slotCount, binCount, minBlockSize, maxBlockSize, initSize, maxSize)) {
798 uint64 id, sysTotal, sysUsage;
806 std::map<uint64, uint64> sentMessages;
809 double totalWriteUS = 0.0;
810 uint64 totalBytes = 0;
811 uint64 totalMsgs = 0;
814 for (n = 0; n<outerCircle; n++) {
817 for (m = 0; m<innerCircle; m++) {
820 data =
new char[dataSize];
821 msg->
setData(
"Data", data, dataSize);
827 unittest::fail(
"TemporalMemory insert memory failed at [%llu][%llu]", n, m);
832 sentMessages[(n * 1000) + m] =
id;
836 totalWriteUS += iterUS;
838 totalMsgs += innerCircle;
841 unittest::detail(
"[%llu] Temporal Memory in use:\t%10s\t[%s allocated] - %.3fms/msg - %.3fMB/sec",
843 (iterUS / (
double)innerCircle) / 1000.0,
844 ((
double)bytes / (iterUS / 1000000.0)) / (1024.0*1024.0)
847 for (m = 0; m<innerCircle; m++) {
850 unittest::fail(
"TemporalMemory retrieve failed at [%llu][%llu]", n, m);
854 unittest::fail(
"TemporalMemory retrieve got invalid data at [%llu][%llu]", n, m);
858 if (msg->
getSerial() != (n * 1000) + m) {
859 unittest::detail(
"[%llu][%llu] TemporalMemory retrieve got wrong message back (%llu != %llu)...",
865 unittest::progress(10 + (
int)((n + 1) * 70 / outerCircle),
"insert / retrieve round-trips");
869 if (totalMsgs && totalWriteUS > 0.0) {
871 ((
double)totalBytes / (totalWriteUS / 1000000.0)) / (1024.0*1024.0),
"MB/s",
true);
873 (totalWriteUS / (
double)totalMsgs) / 1000.0,
"ms",
false);
881 uint32 maintLoops = 20;
882 for (n = 0; n<maintLoops; n++) {
887 unittest::progress(82 + (
int)((n + 1) * 16 / maintLoops),
"expiry / maintenance reclaim");
896 "Temporal shared-memory insert/retrieve, growth and expiry reclaim",
"memory");
Slot/bin shared-memory store for time-limited DataMessages (the node's "dynamic" memory).
#define CHECKTEMPORALMEMORYSERIAL
Re-open the temporal segment if the master's resize serial no longer matches ours (i....
#define SETMSGID(id, node, serial, slot)
Small, dependency-free unit test harness used by all CMSDK object tests.
The central Psyclone data container: a self-contained binary message with typed, named user entries.
bool setTTL(uint64 ttl)
setTTL(uint64 ttl)
DataMessageHeader * data
Pointer to the message's flat memory block (header + user entries).
uint32 getSize()
getSize() Get message size Many types of data of any size can be put into a message as user entries; ...
bool setCreatedTime(uint64 t)
setCreatedTime(uint64 t)
bool isValid()
isValid() Checks that the message memory block exists and carries the current-format object id (DATAM...
uint64 getSerial()
getSerial()
bool setData(const char *key, const char *value, uint32 size)
setData(const char* key, const char* value, uint32 size)
bool setSerial(uint64 serial)
setSerial(uint64 serial)
Handle to the node's master shared-memory segment (MemoryMasterStruct).
bool create(uint16 port)
Create the master segment for the node instance listening on port.
Pure-virtual interface for shared-segment bookkeeping (implemented by MasterMemory).
bool getMemoryUsage(uint64 &alloc, uint64 &usage)
Report allocation/usage of the temporal segment.
bool maintenance()
Expire slots whose EOL has passed and reclaim their blocks.
bool logUsage()
Write current usage statistics to the log.
DataMessage * getCopyOfMessage(uint64 id)
Retrieve a private copy of a stored message.
bool setNodeID(uint16 id)
Set the node id used when composing message ids.
bool insertMessage(DataMessage *msg, uint64 &id)
Copy msg into shared memory until its EOL passes.
bool create(uint32 slotCount, uint16 binCount, uint32 minBlockSize, uint32 maxBlockSize, uint64 initSize, uint64 maxSize, uint32 growSteps=8)
Create the temporal segment (master process only).
TemporalMemory(MemoryController *master)
static bool UnitTest()
Self-test of the temporal store.
bool open()
Attach to the existing temporal segment (size/serial from the master).
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
Recursive mutual-exclusion lock, optionally named for cross-process use.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
int64 GetTimeAge(uint64 t)
Age of a timestamp relative to now.
bool GetLastOccupiedBitLoc(const char *bitfield, uint32 bytesize, uint32 &loc)
Find the highest set (occupied) bit.
bool Sleep(uint32 ms)
Suspend the calling thread.
char * OpenSharedMemorySegment(const char *name, uint64 size)
Open and map an existing named shared memory segment.
char * CreateSharedMemorySegment(const char *name, uint64 size, bool force=false)
Create a named shared memory segment and map it into this process.
double RandomValue()
Uniform random double in [0,1).
bool CloseSharedMemorySegment(char *data, uint64 size)
Unmap a segment previously created/opened here.
bool GetFirstFreeBitLocN(const char *bitfield, uint32 bytesize, uint32 num, uint32 &loc)
Find the first run of num consecutive 0 bits.
std::string BytifySize(double val)
Format a byte count with binary units, e.g.
bool GetFirstFreeBitLoc(const char *bitfield, uint32 bytesize, uint32 &loc)
Find the first 0 (free) bit.
std::string StringFormat(const char *format,...)
printf into a std::string.
bool SetBitN(uint32 loc, uint32 num, bit value, char *bitfield, uint32 bytesize)
Set num consecutive bits starting at loc to value.
bool SetBit(uint32 loc, bit value, char *bitfield, uint32 bytesize)
Set bit loc to value.
uint32 Calc32BitFieldSize(uint32 bitsize)
Compute the byte size needed for a bitfield of bitsize bits, rounded up to a 32-bit boundary.
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
void Register_TemporalMemory_Tests()
Per-message slot record: which bin/blocks hold the message and until when.
uint64 size
Message byte size; 0 means the slot is not in use.
uint64 eol
Absolute end-of-life timestamp (µs); slot reclaimable after this.
uint32 bin
Index of the bin holding the message data.
uint32 startBlock
First block index used within the bin.
uint16 serial
Reuse serial; incremented when the slot is recycled and checked against the serial packed in the mess...
uint64 usage
Bytes actually reserved (blocks used * blocksize >= size).
uint32 blockUsage
Number of contiguous blocks used.
uint64 offset
Byte offset of the message inside the bin's data area.
Root header of the temporal-memory segment.
uint32 cid
Check/magic id validated on attach.