2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/purger.h"
18 #include "hphp/runtime/base/runtime-option.h"
20 #include "hphp/util/compilation-flags.h"
21 #include "hphp/util/logger.h"
22 #include "hphp/util/timer.h"
25 #include <folly/portability/SysMman.h>
26 #include <folly/portability/Unistd.h>
33 * Smoothstep of sigmoidal curves (https://en.wikipedia.org/wiki/Smoothstep)
34 * that grow from 0 to 1 in 0 <= x <= 1.
35 * smoothstep(x) = -2x^3 + 3x^2
37 static double smoothstep(double x
) {
38 return x
* x
* (3 - 2 * x
);
41 // Convert nbytes from Byte to MegaByte
42 static size_t mb(size_t nbytes
) {
47 * Use decay() to determine how much memory we could have at the specific
48 * time slot. And use a lookup table to reduce float calculation.
50 size_t decay(size_t n
, int64_t age
, int64_t window
) {
53 n
- n
* smoothstep(double(age
) / window
);
56 std::atomic
<int> kNextid
{0};
57 const int64_t kTime0
{Timer::GetCurrentTimeMicros()};
58 const int64_t kPageSize
{sysconf(_SC_PAGESIZE
)};
63 : window_
{RuntimeOption::EvalHeapPurgeWindowSize
}
64 , threshold_
{RuntimeOption::EvalHeapPurgeThreshold
}
65 , bucket_slice_
{window_
/ kNumBuckets
}
70 void Purger::purge(char* base
, char* front
) {
71 assert(uintptr_t(base
) % kPageSize
== 0);
72 assert(uintptr_t(front
) % kPageSize
== 0);
73 size_t size
= front
- base
;
74 dirty_
= std::max(size
, dirty_
);
75 // If dirty_ exceeds threshold_, purge all the exceeded memory.
76 if (dirty_
> threshold_
) {
77 madvise(base
+ threshold_
, dirty_
- threshold_
, MADV_DONTNEED
);
81 auto age
= Timer::GetCurrentTimeMicros() - kTime0
;
82 // Update current bucket (reset to size if it expired)
83 auto index
= (age
/ bucket_slice_
) % kNumBuckets
;
84 buckets_
[index
].max
= age
- buckets_
[index
].time
> bucket_slice_
? size
:
85 std::max(buckets_
[index
].max
, size
);
86 buckets_
[index
].time
= age
;
87 // Max of decayed max heap sizes, which means the upper bound that
88 // how much memory can still live in the heap.
90 for (auto& b
: buckets_
) {
91 max
= std::max(max
, decay(b
.max
, age
- b
.time
, window_
));
94 max
= (max
+ kPageSize
- 1) & ~(kPageSize
- 1); // round up
96 // If the dirty memory exceeds max, purge the exceeded part,
97 // otherwise do nothing.
100 VLOG(1) << "purge " << id_
<< ": t " << age
/bucket_slice_
101 << " size " << mb(size
) << " max " << mb(max
)
102 << " dirty " << mb(dirty_
) << " madvise " << mb(dirty_
- max
)
105 madvise(base
+ max
, dirty_
- max
, MADV_DONTNEED
);
109 VLOG(1) << "purge " << id_
<< ": t " << age
/bucket_slice_
110 << " size " << mb(size
)<< " max "<< mb(max
)
111 << " dirty " << mb(dirty_
) << std::endl
;
116 void Purger::flush(char* base
) {
117 assert(uintptr_t(base
) % kPageSize
== 0);
119 auto age
= Timer::GetCurrentTimeMicros() - kTime0
;
120 VLOG(1) << "flush " << id_
<< ": t " << age
/bucket_slice_
121 << " dirty " << mb(dirty_
) << std::endl
;
123 madvise(base
, dirty_
, MADV_DONTNEED
);