Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / base / purger.cpp
blob37f27399225989cf6926e0f248e28017fe096979
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
24 #include <atomic>
25 #include <folly/portability/SysMman.h>
26 #include <folly/portability/Unistd.h>
28 namespace HPHP {
30 namespace {
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) {
43 return nbytes >> 20;
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) {
51 return age <= 0 ? n :
52 age >= window ? 0 :
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)};
62 Purger::Purger()
63 : window_{RuntimeOption::EvalHeapPurgeWindowSize}
64 , threshold_{RuntimeOption::EvalHeapPurgeThreshold}
65 , bucket_slice_{window_ / kNumBuckets}
66 , dirty_{0}
67 , id_{kNextid++}
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);
78 dirty_ = threshold_;
79 return;
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.
89 size_t max = 0;
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.
98 if (dirty_ > max) {
99 if (debug) {
100 VLOG(1) << "purge " << id_ << ": t " << age/bucket_slice_
101 << " size " << mb(size) << " max " << mb(max)
102 << " dirty " << mb(dirty_) << " madvise " << mb(dirty_ - max)
103 << std::endl;
105 madvise(base + max, dirty_ - max, MADV_DONTNEED);
106 dirty_ = max;
107 } else {
108 if (debug) {
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);
118 if (debug) {
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);
124 dirty_ = 0;