/ bytecode / ExecutionCounter.cpp
ExecutionCounter.cpp
  1  /*
  2   * Copyright (C) 2012, 2014, 2016 Apple Inc. All rights reserved.
  3   *
  4   * Redistribution and use in source and binary forms, with or without
  5   * modification, are permitted provided that the following conditions
  6   * are met:
  7   * 1. Redistributions of source code must retain the above copyright
  8   *    notice, this list of conditions and the following disclaimer.
  9   * 2. Redistributions in binary form must reproduce the above copyright
 10   *    notice, this list of conditions and the following disclaimer in the
 11   *    documentation and/or other materials provided with the distribution.
 12   *
 13   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 14   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 15   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 17   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 18   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 19   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 20   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 21   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 23   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 24   */
 25  
 26  #include "config.h"
 27  #include "ExecutionCounter.h"
 28  
 29  #include "CodeBlock.h"
 30  #include "ExecutableAllocator.h"
 31  #include "VMInlines.h"
 32  
 33  namespace JSC {
 34  
 35  template<CountingVariant countingVariant>
 36  ExecutionCounter<countingVariant>::ExecutionCounter()
 37  {
 38      reset();
 39  }
 40  
 41  template<CountingVariant countingVariant>
 42  void ExecutionCounter<countingVariant>::forceSlowPathConcurrently()
 43  {
 44      m_counter = 0;
 45  }
 46  
 47  template<CountingVariant countingVariant>
 48  bool ExecutionCounter<countingVariant>::checkIfThresholdCrossedAndSet(CodeBlock* codeBlock)
 49  {
 50      if (hasCrossedThreshold(codeBlock))
 51          return true;
 52      
 53      if (setThreshold(codeBlock))
 54          return true;
 55      
 56      return false;
 57  }
 58  
 59  template<CountingVariant countingVariant>
 60  void ExecutionCounter<countingVariant>::setNewThreshold(int32_t threshold, CodeBlock* codeBlock)
 61  {
 62      reset();
 63      m_activeThreshold = threshold;
 64      setThreshold(codeBlock);
 65  }
 66  
 67  template<CountingVariant countingVariant>
 68  void ExecutionCounter<countingVariant>::deferIndefinitely()
 69  {
 70      m_totalCount = 0;
 71      m_activeThreshold = std::numeric_limits<int32_t>::max();
 72      m_counter = std::numeric_limits<int32_t>::min();
 73  }
 74  
 75  double applyMemoryUsageHeuristics(int32_t value, CodeBlock* codeBlock)
 76  {
 77      double multiplier = 1.0;
 78      if (codeBlock) {
 79  #if ENABLE(JIT)
 80          multiplier =
 81              ExecutableAllocator::memoryPressureMultiplier(
 82                  codeBlock->baselineAlternative()->predictedMachineCodeSize());
 83  #endif
 84      }
 85      ASSERT(multiplier >= 1.0);
 86      return multiplier * value;
 87  }
 88  
 89  int32_t applyMemoryUsageHeuristicsAndConvertToInt(int32_t value, CodeBlock* codeBlock)
 90  {
 91      double doubleResult = applyMemoryUsageHeuristics(value, codeBlock);
 92      
 93      ASSERT(doubleResult >= 0);
 94      
 95      if (doubleResult > std::numeric_limits<int32_t>::max())
 96          return std::numeric_limits<int32_t>::max();
 97      
 98      return static_cast<int32_t>(doubleResult);
 99  }
100  
101  template<CountingVariant countingVariant>
102  bool ExecutionCounter<countingVariant>::hasCrossedThreshold(CodeBlock* codeBlock) const
103  {
104      // This checks if the current count rounded up to the threshold we were targeting.
105      // For example, if we are using half of available executable memory and have
106      // m_activeThreshold = 1000, applyMemoryUsageHeuristics(m_activeThreshold) will be
107      // 2000, but we will pretend as if the threshold was crossed if we reach 2000 -
108      // 1000 / 2, or 1500. The reasoning here is that we want to avoid thrashing. If
109      // this method returns false, then the JIT's threshold for when it will again call
110      // into the slow path (which will call this method a second time) will be set
111      // according to the difference between the current count and the target count
112      // according to *current* memory usage. But by the time we call into this again, we
113      // may have JIT'ed more code, and so the target count will increase slightly. This
114      // may lead to a repeating pattern where the target count is slightly incremented,
115      // the JIT immediately matches that increase, calls into the slow path again, and
116      // again the target count is slightly incremented. Instead of having this vicious
117      // cycle, we declare victory a bit early if the difference between the current
118      // total and our target according to memory heuristics is small. Our definition of
119      // small is arbitrarily picked to be half of the original threshold (i.e.
120      // m_activeThreshold).
121      
122      double modifiedThreshold = applyMemoryUsageHeuristics(m_activeThreshold, codeBlock);
123      
124      double actualCount = static_cast<double>(m_totalCount) + m_counter;
125      double desiredCount = modifiedThreshold - static_cast<double>(
126          std::min(m_activeThreshold, maximumExecutionCountsBetweenCheckpoints())) / 2;
127      
128      bool result = actualCount >= desiredCount;
129      
130      CODEBLOCK_LOG_EVENT(codeBlock, "thresholdCheck", ("activeThreshold = ", m_activeThreshold, ", modifiedThreshold = ", modifiedThreshold, ", actualCount = ", actualCount, ", desiredCount = ", desiredCount));
131      
132      return result;
133  }
134  
135  template<CountingVariant countingVariant>
136  bool ExecutionCounter<countingVariant>::setThreshold(CodeBlock* codeBlock)
137  {
138      if (m_activeThreshold == std::numeric_limits<int32_t>::max()) {
139          deferIndefinitely();
140          return false;
141      }
142          
143      // Compute the true total count.
144      double trueTotalCount = count();
145      
146      // Correct the threshold for current memory usage.
147      double threshold = applyMemoryUsageHeuristics(m_activeThreshold, codeBlock);
148          
149      // Threshold must be non-negative and not NaN.
150      ASSERT(threshold >= 0);
151          
152      // Adjust the threshold according to the number of executions we have already
153      // seen. This shouldn't go negative, but it might, because of round-off errors.
154      threshold -= trueTotalCount;
155          
156      if (threshold <= 0) {
157          m_counter = 0;
158          m_totalCount = trueTotalCount;
159          return true;
160      }
161  
162      threshold = clippedThreshold(codeBlock ? codeBlock->globalObject() : nullptr, threshold);
163      
164      m_counter = static_cast<int32_t>(-threshold);
165          
166      m_totalCount = trueTotalCount + threshold;
167          
168      return false;
169  }
170  
171  template<CountingVariant countingVariant>
172  void ExecutionCounter<countingVariant>::reset()
173  {
174      m_counter = 0;
175      m_totalCount = 0;
176      m_activeThreshold = 0;
177  }
178  
179  template<CountingVariant countingVariant>
180  void ExecutionCounter<countingVariant>::dump(PrintStream& out) const
181  {
182      out.printf("%lf/%lf, %d", count(), static_cast<double>(m_activeThreshold), m_counter);
183  }
184  
185  template class ExecutionCounter<CountingForBaseline>;
186  template class ExecutionCounter<CountingForUpperTiers>;
187  
188  } // namespace JSC
189