BOOL IsPrime (int n)
{
static int nLast = 1;
static BOOL fLastIsPrime = FALSE;
// if the parameter value has not changed since last time,
// use the finished result
if (n == nLast) return fLastIsPrime;
// calculate and remember the new result
nLast = n;
fLastIsPrime = slow_IsPrime (n);
return fLastIsPrime;
}
slow_IsPrime() call, the other thread that calls IsPrime() will find the values ​​of the nLast and fLastIsPrime inappropriate for each other.nLast = 5, fLastIsPrime = TRUE, and two threads simultaneously call IsPrime(5) , then they have absolutely no need to line up: nothing prevents them from simultaneously using the cached value. #define IsLocked (l) ((l) & 1)
BOOL IsPrime (int n)
{
static int nLast = 1;
static BOOL fLastIsPrime = FALSE;
static LONG lCounter = 0;
// try to take the value from the cache
LONG lCounterStart = InterlockedReadAcquire (& lCounter, -1);
if (! IsLocked (lCounterStart) && n == nLast) {
BOOL fResult = fLastIsPrime;
// nobody touched the cache behind our back?
if (InterlockedReadRelease (& lCounter, -1) == lCounterStart)
return fResult;
}
// either the reading from the cache failed, or the value did not fit:
// calculated in the usual way
BOOL fIsPrime = slow_IsPrime (n);
// try to save the value in the cache
lCounterStart = lCounter;
if (! IsLocked (lCounterStart) &&
InterlockedCompareExchangeAcquire (& lCounter,
lCounterStart + 1, lCounterStart) == lCounterStart) {
nLast = n;
fLastIsPrime = fIsPrime;
InterlockedCompareExchangeRelease (& lCounter,
lCounterStart + 2, lCounterStart + 1);
}
return fIsPrime;
}
lCounter order bit in lCounter means that the cache is “locked” during recording; the remaining bits store the version number. This organization allows you to combine unlocking and updating the version number into one simple operation.lCounter operation Acquire to make sure that the read values nLast and fLastIsPrime were written before the version number. If, judging by the number read, the cache is not blocked, then we read from it the last values ​​of the parameter and the result. If the result fits, we take it; but first you need to make sure that the version number has not changed during the time we read from the cache. If it has changed, then perhaps the values ​​we read do not match each other, so the “suitable” result is in fact unreliable.InterlockedCompareExchangeRelease remove the lock and increase the version number. The operation must be Release, so that changes to the data are recorded in memory before the lock is released.TryEnterCriticalSection : BOOL IsPrime (int n)
{
static int nLast = 1;
static BOOL fLastIsPrime = FALSE;
BOOL fHaveAnswer = FALSE;
BOOL fIsPrime;
// try to take the value from the cache
if (TryEnterCriticalSection (& g_cs)) {
if (n == nLast) {
fHaveAnswer = TRUE;
fIsPrime = fLastIsPrime;
}
LeaveCriticalSection (& g_cs);
}
if (fHaveAnswer) return fIsPrime;
// either the critical section is busy or the value did not fit:
// calculated in the usual way
fIsPrime = slow_IsPrime (n);
// try to save the value in the cache
if (TryEnterCriticalSection (& g_cs)) {
nLast = n;
fLastIsPrime = fIsPrime;
LeaveCriticalSection (& g_cs);
}
return fIsPrime;
}
BOOL IsPrime (int n)
{
static int nLast = 1;
static BOOL fLastIsPrime = FALSE;
BOOL fHaveAnswer = FALSE;
BOOL fIsPrime;
// try to take the value from the cache
if (TryAcquireSRWLockShared (& g_lock)) {
if (n == nLast) {
fHaveAnswer = TRUE;
fIsPrime = fLastIsPrime;
}
ReleaseSRWLockShared (& g_lock);
}
if (fHaveAnswer) return fIsPrime;
// either the cache is locked for writing, or the value did not fit:
// calculated in the usual way
fIsPrime = slow_IsPrime (n);
// try to save the value in the cache
if (TryAcquireSRWLockExclusive (& g_lock)) {
nLast = n;
fLastIsPrime = fIsPrime;
LeaveSRWLockExclusive (& g_lock);
}
return fIsPrime;
}
IsPrime() is called several times with one value (for example, 13), and then many times in a row with another value (for example, 17) - then the head of the streams that check if the result is not cached for 17 simply won't let any stream through to cache the result for 17! It turns out that if the load on the cache is very high (which is exactly why we added it!), Then the implementation based on slim reader-writer locks turns the cache into an almost useless one.Source: https://habr.com/ru/post/118515/
All Articles