blob: 63122f5c4c943e46e5a3749c96d4abcb1ecb18d3 [file] [log] [blame]
<!DOCTYPE HTML>
<html lang="en">
<head>
<!-- Generated by javadoc (17) -->
<title>Source code</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="source: package: org.apache.hadoop.hbase.regionserver, class: DefaultHeapMemoryTuner">
<meta name="generator" content="javadoc/SourceToHTMLConverter">
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
</head>
<body class="source-page">
<main role="main">
<div class="source-container">
<pre><span class="source-line-no">001</span><span id="line-1">/*</span>
<span class="source-line-no">002</span><span id="line-2"> * Licensed to the Apache Software Foundation (ASF) under one</span>
<span class="source-line-no">003</span><span id="line-3"> * or more contributor license agreements. See the NOTICE file</span>
<span class="source-line-no">004</span><span id="line-4"> * distributed with this work for additional information</span>
<span class="source-line-no">005</span><span id="line-5"> * regarding copyright ownership. The ASF licenses this file</span>
<span class="source-line-no">006</span><span id="line-6"> * to you under the Apache License, Version 2.0 (the</span>
<span class="source-line-no">007</span><span id="line-7"> * "License"); you may not use this file except in compliance</span>
<span class="source-line-no">008</span><span id="line-8"> * with the License. You may obtain a copy of the License at</span>
<span class="source-line-no">009</span><span id="line-9"> *</span>
<span class="source-line-no">010</span><span id="line-10"> * http://www.apache.org/licenses/LICENSE-2.0</span>
<span class="source-line-no">011</span><span id="line-11"> *</span>
<span class="source-line-no">012</span><span id="line-12"> * Unless required by applicable law or agreed to in writing, software</span>
<span class="source-line-no">013</span><span id="line-13"> * distributed under the License is distributed on an "AS IS" BASIS,</span>
<span class="source-line-no">014</span><span id="line-14"> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span>
<span class="source-line-no">015</span><span id="line-15"> * See the License for the specific language governing permissions and</span>
<span class="source-line-no">016</span><span id="line-16"> * limitations under the License.</span>
<span class="source-line-no">017</span><span id="line-17"> */</span>
<span class="source-line-no">018</span><span id="line-18">package org.apache.hadoop.hbase.regionserver;</span>
<span class="source-line-no">019</span><span id="line-19"></span>
<span class="source-line-no">020</span><span id="line-20">import static org.apache.hadoop.hbase.HConstants.HFILE_BLOCK_CACHE_SIZE_KEY;</span>
<span class="source-line-no">021</span><span id="line-21">import static org.apache.hadoop.hbase.regionserver.HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY;</span>
<span class="source-line-no">022</span><span id="line-22">import static org.apache.hadoop.hbase.regionserver.HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY;</span>
<span class="source-line-no">023</span><span id="line-23">import static org.apache.hadoop.hbase.regionserver.HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY;</span>
<span class="source-line-no">024</span><span id="line-24">import static org.apache.hadoop.hbase.regionserver.HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY;</span>
<span class="source-line-no">025</span><span id="line-25"></span>
<span class="source-line-no">026</span><span id="line-26">import org.apache.hadoop.conf.Configuration;</span>
<span class="source-line-no">027</span><span id="line-27">import org.apache.hadoop.hbase.HConstants;</span>
<span class="source-line-no">028</span><span id="line-28">import org.apache.hadoop.hbase.io.util.MemorySizeUtil;</span>
<span class="source-line-no">029</span><span id="line-29">import org.apache.hadoop.hbase.regionserver.HeapMemoryManager.TunerContext;</span>
<span class="source-line-no">030</span><span id="line-30">import org.apache.hadoop.hbase.regionserver.HeapMemoryManager.TunerResult;</span>
<span class="source-line-no">031</span><span id="line-31">import org.apache.hadoop.hbase.util.RollingStatCalculator;</span>
<span class="source-line-no">032</span><span id="line-32">import org.apache.yetus.audience.InterfaceAudience;</span>
<span class="source-line-no">033</span><span id="line-33">import org.slf4j.Logger;</span>
<span class="source-line-no">034</span><span id="line-34">import org.slf4j.LoggerFactory;</span>
<span class="source-line-no">035</span><span id="line-35"></span>
<span class="source-line-no">036</span><span id="line-36">/**</span>
<span class="source-line-no">037</span><span id="line-37"> * The default implementation for the HeapMemoryTuner. This will do statistical checks on number of</span>
<span class="source-line-no">038</span><span id="line-38"> * evictions, cache misses and flushes to decide whether there should be changes in the heap size of</span>
<span class="source-line-no">039</span><span id="line-39"> * memstore/block cache. During each tuner operation tuner takes a step which can either be</span>
<span class="source-line-no">040</span><span id="line-40"> * INCREASE_BLOCK_CACHE_SIZE (increase block cache size), INCREASE_MEMSTORE_SIZE (increase memstore</span>
<span class="source-line-no">041</span><span id="line-41"> * size) and by default it is NEUTRAL (no change). We say block cache is sufficient when there is no</span>
<span class="source-line-no">042</span><span id="line-42"> * block cache eviction at all or major amount of memory allocated to block cache is empty,</span>
<span class="source-line-no">043</span><span id="line-43"> * similarly we say memory allocated for memstore is sufficient when there is no memstore flushes</span>
<span class="source-line-no">044</span><span id="line-44"> * because of heap pressure or major amount of memory allocated to memstore is empty. If both are</span>
<span class="source-line-no">045</span><span id="line-45"> * sufficient we do nothing, if exactly one of them is found to be sufficient we decrease its size</span>
<span class="source-line-no">046</span><span id="line-46"> * by &lt;i&gt;step&lt;/i&gt; and increase the other by same amount. If none of them is sufficient we do</span>
<span class="source-line-no">047</span><span id="line-47"> * statistical analysis on number of cache misses and flushes to determine tuner direction. Based on</span>
<span class="source-line-no">048</span><span id="line-48"> * these statistics we decide the tuner direction. If we are not confident about which step</span>
<span class="source-line-no">049</span><span id="line-49"> * direction to take we do nothing and wait for next iteration. On expectation we will be tuning for</span>
<span class="source-line-no">050</span><span id="line-50"> * at least 10% tuner calls. The number of past periods to consider for statistics calculation can</span>
<span class="source-line-no">051</span><span id="line-51"> * be specified in config by &lt;i&gt;hbase.regionserver.heapmemory.autotuner.lookup.periods&lt;/i&gt;. Also</span>
<span class="source-line-no">052</span><span id="line-52"> * these many initial calls to tuner will be ignored (cache is warming up and we leave the system to</span>
<span class="source-line-no">053</span><span id="line-53"> * reach steady state). After the tuner takes a step, in next call we insure that last call was</span>
<span class="source-line-no">054</span><span id="line-54"> * indeed helpful and did not do us any harm. If not then we revert the previous step. The step size</span>
<span class="source-line-no">055</span><span id="line-55"> * is dynamic and it changes based on current and past few tuning directions and their step sizes.</span>
<span class="source-line-no">056</span><span id="line-56"> * We maintain a parameter &lt;i&gt;decayingAvgTunerStepSize&lt;/i&gt; which is sum of past tuner steps with</span>
<span class="source-line-no">057</span><span id="line-57"> * sign(positive for increase in memstore and negative for increase in block cache). But rather than</span>
<span class="source-line-no">058</span><span id="line-58"> * simple sum it is calculated by giving more priority to the recent tuning steps. When last few</span>
<span class="source-line-no">059</span><span id="line-59"> * tuner steps were NETURAL then we assume we are restarting the tuning process and step size is</span>
<span class="source-line-no">060</span><span id="line-60"> * updated to maximum allowed size which can be specified in config by</span>
<span class="source-line-no">061</span><span id="line-61"> * &lt;i&gt;hbase.regionserver.heapmemory.autotuner.step.max&lt;/i&gt;. If in a particular tuning operation the</span>
<span class="source-line-no">062</span><span id="line-62"> * step direction is opposite to what indicated by &lt;i&gt;decayingTunerStepSizeSum&lt;/i&gt; we decrease the</span>
<span class="source-line-no">063</span><span id="line-63"> * step size by half. Step size does not change in other tuning operations. When step size gets</span>
<span class="source-line-no">064</span><span id="line-64"> * below a certain threshold then the following tuner operations are considered to be neutral. The</span>
<span class="source-line-no">065</span><span id="line-65"> * minimum step size can be specified in config by</span>
<span class="source-line-no">066</span><span id="line-66"> * &lt;i&gt;hbase.regionserver.heapmemory.autotuner.step.min&lt;/i&gt;.</span>
<span class="source-line-no">067</span><span id="line-67"> */</span>
<span class="source-line-no">068</span><span id="line-68">@InterfaceAudience.Private</span>
<span class="source-line-no">069</span><span id="line-69">class DefaultHeapMemoryTuner implements HeapMemoryTuner {</span>
<span class="source-line-no">070</span><span id="line-70"> public static final String MAX_STEP_KEY = "hbase.regionserver.heapmemory.autotuner.step.max";</span>
<span class="source-line-no">071</span><span id="line-71"> public static final String MIN_STEP_KEY = "hbase.regionserver.heapmemory.autotuner.step.min";</span>
<span class="source-line-no">072</span><span id="line-72"> public static final String SUFFICIENT_MEMORY_LEVEL_KEY =</span>
<span class="source-line-no">073</span><span id="line-73"> "hbase.regionserver.heapmemory.autotuner.sufficient.memory.level";</span>
<span class="source-line-no">074</span><span id="line-74"> public static final String LOOKUP_PERIODS_KEY =</span>
<span class="source-line-no">075</span><span id="line-75"> "hbase.regionserver.heapmemory.autotuner.lookup.periods";</span>
<span class="source-line-no">076</span><span id="line-76"> public static final String NUM_PERIODS_TO_IGNORE =</span>
<span class="source-line-no">077</span><span id="line-77"> "hbase.regionserver.heapmemory.autotuner.ignored.periods";</span>
<span class="source-line-no">078</span><span id="line-78"> // Maximum step size that the tuner can take</span>
<span class="source-line-no">079</span><span id="line-79"> public static final float DEFAULT_MAX_STEP_VALUE = 0.04f; // 4%</span>
<span class="source-line-no">080</span><span id="line-80"> // Minimum step size that the tuner can take</span>
<span class="source-line-no">081</span><span id="line-81"> public static final float DEFAULT_MIN_STEP_VALUE = 0.00125f; // 0.125%</span>
<span class="source-line-no">082</span><span id="line-82"> // If current block cache size or memstore size in use is below this level relative to memory</span>
<span class="source-line-no">083</span><span id="line-83"> // provided to it then corresponding component will be considered to have sufficient memory</span>
<span class="source-line-no">084</span><span id="line-84"> public static final float DEFAULT_SUFFICIENT_MEMORY_LEVEL_VALUE = 0.5f; // 50%</span>
<span class="source-line-no">085</span><span id="line-85"> // Number of tuner periods that will be considered while calculating mean and deviation</span>
<span class="source-line-no">086</span><span id="line-86"> // If set to zero, all stats will be calculated from the start</span>
<span class="source-line-no">087</span><span id="line-87"> public static final int DEFAULT_LOOKUP_PERIODS = 60;</span>
<span class="source-line-no">088</span><span id="line-88"> public static final int DEFAULT_NUM_PERIODS_IGNORED = 60;</span>
<span class="source-line-no">089</span><span id="line-89"> private static final TunerResult NO_OP_TUNER_RESULT = new TunerResult(false);</span>
<span class="source-line-no">090</span><span id="line-90"> // If deviation of tuner step size gets below this value then it means past few periods were</span>
<span class="source-line-no">091</span><span id="line-91"> // NEUTRAL(given that last tuner period was also NEUTRAL).</span>
<span class="source-line-no">092</span><span id="line-92"> private static final double TUNER_STEP_EPS = 1e-6;</span>
<span class="source-line-no">093</span><span id="line-93"></span>
<span class="source-line-no">094</span><span id="line-94"> private Logger LOG = LoggerFactory.getLogger(DefaultHeapMemoryTuner.class);</span>
<span class="source-line-no">095</span><span id="line-95"> private TunerResult TUNER_RESULT = new TunerResult(true);</span>
<span class="source-line-no">096</span><span id="line-96"> private Configuration conf;</span>
<span class="source-line-no">097</span><span id="line-97"> private float sufficientMemoryLevel = DEFAULT_SUFFICIENT_MEMORY_LEVEL_VALUE;</span>
<span class="source-line-no">098</span><span id="line-98"> private float maximumStepSize = DEFAULT_MAX_STEP_VALUE;</span>
<span class="source-line-no">099</span><span id="line-99"> private float minimumStepSize = DEFAULT_MIN_STEP_VALUE;</span>
<span class="source-line-no">100</span><span id="line-100"> private int tunerLookupPeriods = DEFAULT_LOOKUP_PERIODS;</span>
<span class="source-line-no">101</span><span id="line-101"> private int numPeriodsToIgnore = DEFAULT_NUM_PERIODS_IGNORED;</span>
<span class="source-line-no">102</span><span id="line-102"> // Counter to ignore few initial periods while cache is still warming up</span>
<span class="source-line-no">103</span><span id="line-103"> // Memory tuner will do no operation for the first "tunerLookupPeriods"</span>
<span class="source-line-no">104</span><span id="line-104"> private int ignoreInitialPeriods = 0;</span>
<span class="source-line-no">105</span><span id="line-105"></span>
<span class="source-line-no">106</span><span id="line-106"> private float globalMemStorePercentMinRange;</span>
<span class="source-line-no">107</span><span id="line-107"> private float globalMemStorePercentMaxRange;</span>
<span class="source-line-no">108</span><span id="line-108"> private float blockCachePercentMinRange;</span>
<span class="source-line-no">109</span><span id="line-109"> private float blockCachePercentMaxRange;</span>
<span class="source-line-no">110</span><span id="line-110"></span>
<span class="source-line-no">111</span><span id="line-111"> private float globalMemStoreLimitLowMarkPercent;</span>
<span class="source-line-no">112</span><span id="line-112"></span>
<span class="source-line-no">113</span><span id="line-113"> // Store statistics about the corresponding parameters for memory tuning</span>
<span class="source-line-no">114</span><span id="line-114"> private RollingStatCalculator rollingStatsForCacheMisses;</span>
<span class="source-line-no">115</span><span id="line-115"> private RollingStatCalculator rollingStatsForFlushes;</span>
<span class="source-line-no">116</span><span id="line-116"> private RollingStatCalculator rollingStatsForEvictions;</span>
<span class="source-line-no">117</span><span id="line-117"> private RollingStatCalculator rollingStatsForTunerSteps;</span>
<span class="source-line-no">118</span><span id="line-118"> // Set step size to max value for tuning, this step size will adjust dynamically while tuning</span>
<span class="source-line-no">119</span><span id="line-119"> private float step = DEFAULT_MAX_STEP_VALUE;</span>
<span class="source-line-no">120</span><span id="line-120"> private StepDirection prevTuneDirection = StepDirection.NEUTRAL;</span>
<span class="source-line-no">121</span><span id="line-121"> // positive means memstore's size was increased</span>
<span class="source-line-no">122</span><span id="line-122"> // It is not just arithmetic sum of past tuner periods. More priority is given to recent</span>
<span class="source-line-no">123</span><span id="line-123"> // tuning steps.</span>
<span class="source-line-no">124</span><span id="line-124"> private double decayingTunerStepSizeSum = 0;</span>
<span class="source-line-no">125</span><span id="line-125"></span>
<span class="source-line-no">126</span><span id="line-126"> @Override</span>
<span class="source-line-no">127</span><span id="line-127"> public TunerResult tune(TunerContext context) {</span>
<span class="source-line-no">128</span><span id="line-128"> float curMemstoreSize = context.getCurMemStoreSize();</span>
<span class="source-line-no">129</span><span id="line-129"> float curBlockCacheSize = context.getCurBlockCacheSize();</span>
<span class="source-line-no">130</span><span id="line-130"> addToRollingStats(context);</span>
<span class="source-line-no">131</span><span id="line-131"></span>
<span class="source-line-no">132</span><span id="line-132"> if (ignoreInitialPeriods &lt; numPeriodsToIgnore) {</span>
<span class="source-line-no">133</span><span id="line-133"> // Ignoring the first few tuner periods</span>
<span class="source-line-no">134</span><span id="line-134"> ignoreInitialPeriods++;</span>
<span class="source-line-no">135</span><span id="line-135"> rollingStatsForTunerSteps.insertDataValue(0);</span>
<span class="source-line-no">136</span><span id="line-136"> LOG.info("Ignoring initial tuning periods: {} so far, {} to ignore", ignoreInitialPeriods,</span>
<span class="source-line-no">137</span><span id="line-137"> numPeriodsToIgnore);</span>
<span class="source-line-no">138</span><span id="line-138"> return NO_OP_TUNER_RESULT;</span>
<span class="source-line-no">139</span><span id="line-139"> }</span>
<span class="source-line-no">140</span><span id="line-140"> StepDirection newTuneDirection = getTuneDirection(context);</span>
<span class="source-line-no">141</span><span id="line-141"></span>
<span class="source-line-no">142</span><span id="line-142"> long blockedFlushCount = context.getBlockedFlushCount();</span>
<span class="source-line-no">143</span><span id="line-143"> long unblockedFlushCount = context.getUnblockedFlushCount();</span>
<span class="source-line-no">144</span><span id="line-144"> long totalOnheapFlushCount = blockedFlushCount + unblockedFlushCount;</span>
<span class="source-line-no">145</span><span id="line-145"> boolean offheapMemstore = context.isOffheapMemStore();</span>
<span class="source-line-no">146</span><span id="line-146"> float newMemstoreSize;</span>
<span class="source-line-no">147</span><span id="line-147"> float newBlockCacheSize;</span>
<span class="source-line-no">148</span><span id="line-148"></span>
<span class="source-line-no">149</span><span id="line-149"> // Adjusting step size for tuning to get to steady state or restart from steady state.</span>
<span class="source-line-no">150</span><span id="line-150"> // Even if the step size was 4% and 32 GB memory size, we will be shifting 1 GB back and forth</span>
<span class="source-line-no">151</span><span id="line-151"> // per tuner operation and it can affect the performance of cluster so we keep on decreasing</span>
<span class="source-line-no">152</span><span id="line-152"> // step size until everything settles.</span>
<span class="source-line-no">153</span><span id="line-153"> if (</span>
<span class="source-line-no">154</span><span id="line-154"> prevTuneDirection == StepDirection.NEUTRAL &amp;&amp; newTuneDirection != StepDirection.NEUTRAL</span>
<span class="source-line-no">155</span><span id="line-155"> &amp;&amp; rollingStatsForTunerSteps.getDeviation() &lt; TUNER_STEP_EPS</span>
<span class="source-line-no">156</span><span id="line-156"> ) {</span>
<span class="source-line-no">157</span><span id="line-157"> // Restarting the tuning from steady state and setting step size to maximum.</span>
<span class="source-line-no">158</span><span id="line-158"> // The deviation cannot be that low if last period was neutral and some recent periods were</span>
<span class="source-line-no">159</span><span id="line-159"> // not neutral.</span>
<span class="source-line-no">160</span><span id="line-160"> step = maximumStepSize;</span>
<span class="source-line-no">161</span><span id="line-161"> } else if (</span>
<span class="source-line-no">162</span><span id="line-162"> (newTuneDirection == StepDirection.INCREASE_MEMSTORE_SIZE &amp;&amp; decayingTunerStepSizeSum &lt; 0)</span>
<span class="source-line-no">163</span><span id="line-163"> || (newTuneDirection == StepDirection.INCREASE_BLOCK_CACHE_SIZE</span>
<span class="source-line-no">164</span><span id="line-164"> &amp;&amp; decayingTunerStepSizeSum &gt; 0)</span>
<span class="source-line-no">165</span><span id="line-165"> ) {</span>
<span class="source-line-no">166</span><span id="line-166"> // Current step is opposite of past tuner actions so decrease the step size to reach steady</span>
<span class="source-line-no">167</span><span id="line-167"> // state.</span>
<span class="source-line-no">168</span><span id="line-168"> if (!offheapMemstore &amp;&amp; step != minimumStepSize) {</span>
<span class="source-line-no">169</span><span id="line-169"> // we leave the step to be at minimumStepSize for offheap memstore</span>
<span class="source-line-no">170</span><span id="line-170"> step = step / 2.00f;</span>
<span class="source-line-no">171</span><span id="line-171"> }</span>
<span class="source-line-no">172</span><span id="line-172"> }</span>
<span class="source-line-no">173</span><span id="line-173"> if (step &lt; minimumStepSize) {</span>
<span class="source-line-no">174</span><span id="line-174"> // If step size is too small then we do nothing.</span>
<span class="source-line-no">175</span><span id="line-175"> LOG.debug("Tuner step size is too low; we will not perform any tuning this time.");</span>
<span class="source-line-no">176</span><span id="line-176"> step = 0.0f;</span>
<span class="source-line-no">177</span><span id="line-177"> newTuneDirection = StepDirection.NEUTRAL;</span>
<span class="source-line-no">178</span><span id="line-178"> }</span>
<span class="source-line-no">179</span><span id="line-179"> // There are no flushes due to onheap pressure and</span>
<span class="source-line-no">180</span><span id="line-180"> // we have an offheap memstore and we are in need of more block_cache size.</span>
<span class="source-line-no">181</span><span id="line-181"> if (</span>
<span class="source-line-no">182</span><span id="line-182"> totalOnheapFlushCount == 0 &amp;&amp; offheapMemstore</span>
<span class="source-line-no">183</span><span id="line-183"> &amp;&amp; newTuneDirection == StepDirection.INCREASE_BLOCK_CACHE_SIZE</span>
<span class="source-line-no">184</span><span id="line-184"> ) {</span>
<span class="source-line-no">185</span><span id="line-185"> // we are sure that there are flushes only due to offheap pressure</span>
<span class="source-line-no">186</span><span id="line-186"> // So don't do the memstore decrease equal to the step size. Instead do minimum stepSize</span>
<span class="source-line-no">187</span><span id="line-187"> // decrease. But even if we have some flushes due to heap then it is better we tune</span>
<span class="source-line-no">188</span><span id="line-188"> // the existing way.</span>
<span class="source-line-no">189</span><span id="line-189"> step = minimumStepSize;</span>
<span class="source-line-no">190</span><span id="line-190"> }</span>
<span class="source-line-no">191</span><span id="line-191"> // Increase / decrease the memstore / block cache sizes depending on new tuner step.</span>
<span class="source-line-no">192</span><span id="line-192"> // We don't want to exert immediate pressure on memstore. So, we decrease its size gracefully;</span>
<span class="source-line-no">193</span><span id="line-193"> // we set a minimum bar in the middle of the total memstore size and the lower limit.</span>
<span class="source-line-no">194</span><span id="line-194"> float minMemstoreSize = ((globalMemStoreLimitLowMarkPercent + 1) * curMemstoreSize) / 2.00f;</span>
<span class="source-line-no">195</span><span id="line-195"></span>
<span class="source-line-no">196</span><span id="line-196"> switch (newTuneDirection) {</span>
<span class="source-line-no">197</span><span id="line-197"> case INCREASE_BLOCK_CACHE_SIZE:</span>
<span class="source-line-no">198</span><span id="line-198"> if (curMemstoreSize - step &lt; minMemstoreSize) {</span>
<span class="source-line-no">199</span><span id="line-199"> step = curMemstoreSize - minMemstoreSize;</span>
<span class="source-line-no">200</span><span id="line-200"> }</span>
<span class="source-line-no">201</span><span id="line-201"> newMemstoreSize = curMemstoreSize - step;</span>
<span class="source-line-no">202</span><span id="line-202"> newBlockCacheSize = curBlockCacheSize + step;</span>
<span class="source-line-no">203</span><span id="line-203"> rollingStatsForTunerSteps.insertDataValue(-(int) (step * 100000));</span>
<span class="source-line-no">204</span><span id="line-204"> decayingTunerStepSizeSum = (decayingTunerStepSizeSum - step) / 2.00f;</span>
<span class="source-line-no">205</span><span id="line-205"> break;</span>
<span class="source-line-no">206</span><span id="line-206"> case INCREASE_MEMSTORE_SIZE:</span>
<span class="source-line-no">207</span><span id="line-207"> newBlockCacheSize = curBlockCacheSize - step;</span>
<span class="source-line-no">208</span><span id="line-208"> newMemstoreSize = curMemstoreSize + step;</span>
<span class="source-line-no">209</span><span id="line-209"> rollingStatsForTunerSteps.insertDataValue((int) (step * 100000));</span>
<span class="source-line-no">210</span><span id="line-210"> decayingTunerStepSizeSum = (decayingTunerStepSizeSum + step) / 2.00f;</span>
<span class="source-line-no">211</span><span id="line-211"> break;</span>
<span class="source-line-no">212</span><span id="line-212"> default:</span>
<span class="source-line-no">213</span><span id="line-213"> prevTuneDirection = StepDirection.NEUTRAL;</span>
<span class="source-line-no">214</span><span id="line-214"> rollingStatsForTunerSteps.insertDataValue(0);</span>
<span class="source-line-no">215</span><span id="line-215"> decayingTunerStepSizeSum = (decayingTunerStepSizeSum) / 2.00f;</span>
<span class="source-line-no">216</span><span id="line-216"> return NO_OP_TUNER_RESULT;</span>
<span class="source-line-no">217</span><span id="line-217"> }</span>
<span class="source-line-no">218</span><span id="line-218"> // Check we are within max/min bounds.</span>
<span class="source-line-no">219</span><span id="line-219"> if (newMemstoreSize &gt; globalMemStorePercentMaxRange) {</span>
<span class="source-line-no">220</span><span id="line-220"> newMemstoreSize = globalMemStorePercentMaxRange;</span>
<span class="source-line-no">221</span><span id="line-221"> } else if (newMemstoreSize &lt; globalMemStorePercentMinRange) {</span>
<span class="source-line-no">222</span><span id="line-222"> newMemstoreSize = globalMemStorePercentMinRange;</span>
<span class="source-line-no">223</span><span id="line-223"> }</span>
<span class="source-line-no">224</span><span id="line-224"> if (newBlockCacheSize &gt; blockCachePercentMaxRange) {</span>
<span class="source-line-no">225</span><span id="line-225"> newBlockCacheSize = blockCachePercentMaxRange;</span>
<span class="source-line-no">226</span><span id="line-226"> } else if (newBlockCacheSize &lt; blockCachePercentMinRange) {</span>
<span class="source-line-no">227</span><span id="line-227"> newBlockCacheSize = blockCachePercentMinRange;</span>
<span class="source-line-no">228</span><span id="line-228"> }</span>
<span class="source-line-no">229</span><span id="line-229"> TUNER_RESULT.setBlockCacheSize(newBlockCacheSize);</span>
<span class="source-line-no">230</span><span id="line-230"> TUNER_RESULT.setMemStoreSize(newMemstoreSize);</span>
<span class="source-line-no">231</span><span id="line-231"> prevTuneDirection = newTuneDirection;</span>
<span class="source-line-no">232</span><span id="line-232"> return TUNER_RESULT;</span>
<span class="source-line-no">233</span><span id="line-233"> }</span>
<span class="source-line-no">234</span><span id="line-234"></span>
<span class="source-line-no">235</span><span id="line-235"> /**</span>
<span class="source-line-no">236</span><span id="line-236"> * Determine best direction of tuning base on given context.</span>
<span class="source-line-no">237</span><span id="line-237"> * @param context The tuner context.</span>
<span class="source-line-no">238</span><span id="line-238"> * @return tuning direction.</span>
<span class="source-line-no">239</span><span id="line-239"> */</span>
<span class="source-line-no">240</span><span id="line-240"> private StepDirection getTuneDirection(TunerContext context) {</span>
<span class="source-line-no">241</span><span id="line-241"> StepDirection newTuneDirection = StepDirection.NEUTRAL;</span>
<span class="source-line-no">242</span><span id="line-242"> long blockedFlushCount = context.getBlockedFlushCount();</span>
<span class="source-line-no">243</span><span id="line-243"> long unblockedFlushCount = context.getUnblockedFlushCount();</span>
<span class="source-line-no">244</span><span id="line-244"> long evictCount = context.getEvictCount();</span>
<span class="source-line-no">245</span><span id="line-245"> long cacheMissCount = context.getCacheMissCount();</span>
<span class="source-line-no">246</span><span id="line-246"> long totalFlushCount = blockedFlushCount + unblockedFlushCount;</span>
<span class="source-line-no">247</span><span id="line-247"> float curMemstoreSize = context.getCurMemStoreSize();</span>
<span class="source-line-no">248</span><span id="line-248"> float curBlockCacheSize = context.getCurBlockCacheSize();</span>
<span class="source-line-no">249</span><span id="line-249"> StringBuilder tunerLog = new StringBuilder();</span>
<span class="source-line-no">250</span><span id="line-250"> // We can consider memstore or block cache to be sufficient if</span>
<span class="source-line-no">251</span><span id="line-251"> // we are using only a minor fraction of what have been already provided to it.</span>
<span class="source-line-no">252</span><span id="line-252"> boolean earlyMemstoreSufficientCheck = totalFlushCount == 0</span>
<span class="source-line-no">253</span><span id="line-253"> || context.getCurMemStoreUsed() &lt; curMemstoreSize * sufficientMemoryLevel;</span>
<span class="source-line-no">254</span><span id="line-254"> boolean earlyBlockCacheSufficientCheck =</span>
<span class="source-line-no">255</span><span id="line-255"> evictCount == 0 || context.getCurBlockCacheUsed() &lt; curBlockCacheSize * sufficientMemoryLevel;</span>
<span class="source-line-no">256</span><span id="line-256"> if (earlyMemstoreSufficientCheck &amp;&amp; earlyBlockCacheSufficientCheck) {</span>
<span class="source-line-no">257</span><span id="line-257"> // Both memstore and block cache memory seems to be sufficient. No operation required.</span>
<span class="source-line-no">258</span><span id="line-258"> newTuneDirection = StepDirection.NEUTRAL;</span>
<span class="source-line-no">259</span><span id="line-259"> tunerLog.append("Going to do nothing because no changes are needed.");</span>
<span class="source-line-no">260</span><span id="line-260"> } else if (earlyMemstoreSufficientCheck) {</span>
<span class="source-line-no">261</span><span id="line-261"> // Increase the block cache size and corresponding decrease in memstore size.</span>
<span class="source-line-no">262</span><span id="line-262"> newTuneDirection = StepDirection.INCREASE_BLOCK_CACHE_SIZE;</span>
<span class="source-line-no">263</span><span id="line-263"> tunerLog.append("Going to increase the block cache size.");</span>
<span class="source-line-no">264</span><span id="line-264"> } else if (earlyBlockCacheSufficientCheck) {</span>
<span class="source-line-no">265</span><span id="line-265"> // Increase the memstore size and corresponding decrease in block cache size.</span>
<span class="source-line-no">266</span><span id="line-266"> newTuneDirection = StepDirection.INCREASE_MEMSTORE_SIZE;</span>
<span class="source-line-no">267</span><span id="line-267"> tunerLog.append("Going to increase the memstore size.");</span>
<span class="source-line-no">268</span><span id="line-268"> } else {</span>
<span class="source-line-no">269</span><span id="line-269"> // Early checks for sufficient memory failed. Tuning memory based on past statistics.</span>
<span class="source-line-no">270</span><span id="line-270"> // Boolean indicator to show if we need to revert previous step or not.</span>
<span class="source-line-no">271</span><span id="line-271"> boolean isReverting = false;</span>
<span class="source-line-no">272</span><span id="line-272"> switch (prevTuneDirection) {</span>
<span class="source-line-no">273</span><span id="line-273"> // Here we are using number of evictions rather than cache misses because it is more</span>
<span class="source-line-no">274</span><span id="line-274"> // strong indicator for deficient cache size. Improving caching is what we</span>
<span class="source-line-no">275</span><span id="line-275"> // would like to optimize for in steady state.</span>
<span class="source-line-no">276</span><span id="line-276"> case INCREASE_BLOCK_CACHE_SIZE:</span>
<span class="source-line-no">277</span><span id="line-277"> if (</span>
<span class="source-line-no">278</span><span id="line-278"> (double) evictCount &gt; rollingStatsForEvictions.getMean() || (double) totalFlushCount</span>
<span class="source-line-no">279</span><span id="line-279"> &gt; rollingStatsForFlushes.getMean() + rollingStatsForFlushes.getDeviation() / 2.00</span>
<span class="source-line-no">280</span><span id="line-280"> ) {</span>
<span class="source-line-no">281</span><span id="line-281"> // Reverting previous step as it was not useful.</span>
<span class="source-line-no">282</span><span id="line-282"> // Tuning failed to decrease evictions or tuning resulted in large number of flushes.</span>
<span class="source-line-no">283</span><span id="line-283"> newTuneDirection = StepDirection.INCREASE_MEMSTORE_SIZE;</span>
<span class="source-line-no">284</span><span id="line-284"> tunerLog.append("We will revert previous tuning");</span>
<span class="source-line-no">285</span><span id="line-285"> if ((double) evictCount &gt; rollingStatsForEvictions.getMean()) {</span>
<span class="source-line-no">286</span><span id="line-286"> tunerLog.append(" because we could not decrease evictions sufficiently.");</span>
<span class="source-line-no">287</span><span id="line-287"> } else {</span>
<span class="source-line-no">288</span><span id="line-288"> tunerLog.append(" because the number of flushes rose significantly.");</span>
<span class="source-line-no">289</span><span id="line-289"> }</span>
<span class="source-line-no">290</span><span id="line-290"> isReverting = true;</span>
<span class="source-line-no">291</span><span id="line-291"> }</span>
<span class="source-line-no">292</span><span id="line-292"> break;</span>
<span class="source-line-no">293</span><span id="line-293"> case INCREASE_MEMSTORE_SIZE:</span>
<span class="source-line-no">294</span><span id="line-294"> if (</span>
<span class="source-line-no">295</span><span id="line-295"> (double) totalFlushCount &gt; rollingStatsForFlushes.getMean()</span>
<span class="source-line-no">296</span><span id="line-296"> || (double) evictCount &gt; rollingStatsForEvictions.getMean()</span>
<span class="source-line-no">297</span><span id="line-297"> + rollingStatsForEvictions.getDeviation() / 2.00</span>
<span class="source-line-no">298</span><span id="line-298"> ) {</span>
<span class="source-line-no">299</span><span id="line-299"> // Reverting previous step as it was not useful.</span>
<span class="source-line-no">300</span><span id="line-300"> // Tuning failed to decrease flushes or tuning resulted in large number of evictions.</span>
<span class="source-line-no">301</span><span id="line-301"> newTuneDirection = StepDirection.INCREASE_BLOCK_CACHE_SIZE;</span>
<span class="source-line-no">302</span><span id="line-302"> tunerLog.append("We will revert previous tuning");</span>
<span class="source-line-no">303</span><span id="line-303"> if ((double) totalFlushCount &gt; rollingStatsForFlushes.getMean()) {</span>
<span class="source-line-no">304</span><span id="line-304"> tunerLog.append(" because we could not decrease flushes sufficiently.");</span>
<span class="source-line-no">305</span><span id="line-305"> } else {</span>
<span class="source-line-no">306</span><span id="line-306"> tunerLog.append(" because number of evictions rose significantly.");</span>
<span class="source-line-no">307</span><span id="line-307"> }</span>
<span class="source-line-no">308</span><span id="line-308"> isReverting = true;</span>
<span class="source-line-no">309</span><span id="line-309"> }</span>
<span class="source-line-no">310</span><span id="line-310"> break;</span>
<span class="source-line-no">311</span><span id="line-311"> default:</span>
<span class="source-line-no">312</span><span id="line-312"> // Last step was neutral, revert doesn't not apply here.</span>
<span class="source-line-no">313</span><span id="line-313"> break;</span>
<span class="source-line-no">314</span><span id="line-314"> }</span>
<span class="source-line-no">315</span><span id="line-315"> // If we are not reverting. We try to tune memory sizes by looking at cache misses / flushes.</span>
<span class="source-line-no">316</span><span id="line-316"> if (!isReverting) {</span>
<span class="source-line-no">317</span><span id="line-317"> // mean +- deviation*0.8 is considered to be normal</span>
<span class="source-line-no">318</span><span id="line-318"> // below it its consider low and above it is considered high.</span>
<span class="source-line-no">319</span><span id="line-319"> // We can safely assume that the number cache misses, flushes are normally distributed over</span>
<span class="source-line-no">320</span><span id="line-320"> // past periods and hence on all the above mentioned classes (normal, high and low)</span>
<span class="source-line-no">321</span><span id="line-321"> // are likely to occur with probability 56%, 22%, 22% respectively. Hence there is at</span>
<span class="source-line-no">322</span><span id="line-322"> // least ~10% probability that we will not fall in NEUTRAL step.</span>
<span class="source-line-no">323</span><span id="line-323"> // This optimization solution is feedback based and we revert when we</span>
<span class="source-line-no">324</span><span id="line-324"> // dont find our steps helpful. Hence we want to do tuning only when we have clear</span>
<span class="source-line-no">325</span><span id="line-325"> // indications because too many unnecessary tuning may affect the performance of cluster.</span>
<span class="source-line-no">326</span><span id="line-326"> if (</span>
<span class="source-line-no">327</span><span id="line-327"> (double) cacheMissCount</span>
<span class="source-line-no">328</span><span id="line-328"> &lt; rollingStatsForCacheMisses.getMean()</span>
<span class="source-line-no">329</span><span id="line-329"> - rollingStatsForCacheMisses.getDeviation() * 0.80</span>
<span class="source-line-no">330</span><span id="line-330"> &amp;&amp; (double) totalFlushCount</span>
<span class="source-line-no">331</span><span id="line-331"> &lt; rollingStatsForFlushes.getMean() - rollingStatsForFlushes.getDeviation() * 0.80</span>
<span class="source-line-no">332</span><span id="line-332"> ) {</span>
<span class="source-line-no">333</span><span id="line-333"> // Everything is fine no tuning required</span>
<span class="source-line-no">334</span><span id="line-334"> newTuneDirection = StepDirection.NEUTRAL;</span>
<span class="source-line-no">335</span><span id="line-335"> } else if (</span>
<span class="source-line-no">336</span><span id="line-336"> (double) cacheMissCount</span>
<span class="source-line-no">337</span><span id="line-337"> &gt; rollingStatsForCacheMisses.getMean()</span>
<span class="source-line-no">338</span><span id="line-338"> + rollingStatsForCacheMisses.getDeviation() * 0.80</span>
<span class="source-line-no">339</span><span id="line-339"> &amp;&amp; (double) totalFlushCount</span>
<span class="source-line-no">340</span><span id="line-340"> &lt; rollingStatsForFlushes.getMean() - rollingStatsForFlushes.getDeviation() * 0.80</span>
<span class="source-line-no">341</span><span id="line-341"> ) {</span>
<span class="source-line-no">342</span><span id="line-342"> // more misses , increasing cache size</span>
<span class="source-line-no">343</span><span id="line-343"> newTuneDirection = StepDirection.INCREASE_BLOCK_CACHE_SIZE;</span>
<span class="source-line-no">344</span><span id="line-344"> tunerLog.append(</span>
<span class="source-line-no">345</span><span id="line-345"> "Going to increase block cache size due to increase in number of cache misses.");</span>
<span class="source-line-no">346</span><span id="line-346"> } else if (</span>
<span class="source-line-no">347</span><span id="line-347"> (double) cacheMissCount</span>
<span class="source-line-no">348</span><span id="line-348"> &lt; rollingStatsForCacheMisses.getMean()</span>
<span class="source-line-no">349</span><span id="line-349"> - rollingStatsForCacheMisses.getDeviation() * 0.80</span>
<span class="source-line-no">350</span><span id="line-350"> &amp;&amp; (double) totalFlushCount</span>
<span class="source-line-no">351</span><span id="line-351"> &gt; rollingStatsForFlushes.getMean() + rollingStatsForFlushes.getDeviation() * 0.80</span>
<span class="source-line-no">352</span><span id="line-352"> ) {</span>
<span class="source-line-no">353</span><span id="line-353"> // more flushes , increasing memstore size</span>
<span class="source-line-no">354</span><span id="line-354"> newTuneDirection = StepDirection.INCREASE_MEMSTORE_SIZE;</span>
<span class="source-line-no">355</span><span id="line-355"> tunerLog.append("Going to increase memstore size due to increase in number of flushes.");</span>
<span class="source-line-no">356</span><span id="line-356"> } else if (blockedFlushCount &gt; 0 &amp;&amp; prevTuneDirection == StepDirection.NEUTRAL) {</span>
<span class="source-line-no">357</span><span id="line-357"> // we do not want blocked flushes</span>
<span class="source-line-no">358</span><span id="line-358"> newTuneDirection = StepDirection.INCREASE_MEMSTORE_SIZE;</span>
<span class="source-line-no">359</span><span id="line-359"> tunerLog.append(</span>
<span class="source-line-no">360</span><span id="line-360"> "Going to increase memstore size due to" + blockedFlushCount + " blocked flushes.");</span>
<span class="source-line-no">361</span><span id="line-361"> } else {</span>
<span class="source-line-no">362</span><span id="line-362"> // Default. Not enough facts to do tuning.</span>
<span class="source-line-no">363</span><span id="line-363"> tunerLog.append(</span>
<span class="source-line-no">364</span><span id="line-364"> "Going to do nothing because we " + "could not determine best tuning direction");</span>
<span class="source-line-no">365</span><span id="line-365"> newTuneDirection = StepDirection.NEUTRAL;</span>
<span class="source-line-no">366</span><span id="line-366"> }</span>
<span class="source-line-no">367</span><span id="line-367"> }</span>
<span class="source-line-no">368</span><span id="line-368"> }</span>
<span class="source-line-no">369</span><span id="line-369"> // Log NEUTRAL decisions at DEBUG, because they are the most frequent and not that interesting.</span>
<span class="source-line-no">370</span><span id="line-370"> // Log other decisions at INFO because they are making meaningful operational changes.</span>
<span class="source-line-no">371</span><span id="line-371"> switch (newTuneDirection) {</span>
<span class="source-line-no">372</span><span id="line-372"> case NEUTRAL:</span>
<span class="source-line-no">373</span><span id="line-373"> if (LOG.isDebugEnabled()) {</span>
<span class="source-line-no">374</span><span id="line-374"> LOG.debug(tunerLog.toString());</span>
<span class="source-line-no">375</span><span id="line-375"> }</span>
<span class="source-line-no">376</span><span id="line-376"> break;</span>
<span class="source-line-no">377</span><span id="line-377"> default:</span>
<span class="source-line-no">378</span><span id="line-378"> LOG.info(tunerLog.toString());</span>
<span class="source-line-no">379</span><span id="line-379"> break;</span>
<span class="source-line-no">380</span><span id="line-380"> }</span>
<span class="source-line-no">381</span><span id="line-381"> return newTuneDirection;</span>
<span class="source-line-no">382</span><span id="line-382"> }</span>
<span class="source-line-no">383</span><span id="line-383"></span>
<span class="source-line-no">384</span><span id="line-384"> /**</span>
<span class="source-line-no">385</span><span id="line-385"> * Add the given context to the rolling tuner stats.</span>
<span class="source-line-no">386</span><span id="line-386"> * @param context The tuner context.</span>
<span class="source-line-no">387</span><span id="line-387"> */</span>
<span class="source-line-no">388</span><span id="line-388"> private void addToRollingStats(TunerContext context) {</span>
<span class="source-line-no">389</span><span id="line-389"> rollingStatsForCacheMisses.insertDataValue(context.getCacheMissCount());</span>
<span class="source-line-no">390</span><span id="line-390"> rollingStatsForFlushes</span>
<span class="source-line-no">391</span><span id="line-391"> .insertDataValue(context.getBlockedFlushCount() + context.getUnblockedFlushCount());</span>
<span class="source-line-no">392</span><span id="line-392"> rollingStatsForEvictions.insertDataValue(context.getEvictCount());</span>
<span class="source-line-no">393</span><span id="line-393"> }</span>
<span class="source-line-no">394</span><span id="line-394"></span>
<span class="source-line-no">395</span><span id="line-395"> @Override</span>
<span class="source-line-no">396</span><span id="line-396"> public Configuration getConf() {</span>
<span class="source-line-no">397</span><span id="line-397"> return this.conf;</span>
<span class="source-line-no">398</span><span id="line-398"> }</span>
<span class="source-line-no">399</span><span id="line-399"></span>
<span class="source-line-no">400</span><span id="line-400"> @Override</span>
<span class="source-line-no">401</span><span id="line-401"> public void setConf(Configuration conf) {</span>
<span class="source-line-no">402</span><span id="line-402"> this.conf = conf;</span>
<span class="source-line-no">403</span><span id="line-403"> this.maximumStepSize = conf.getFloat(MAX_STEP_KEY, DEFAULT_MAX_STEP_VALUE);</span>
<span class="source-line-no">404</span><span id="line-404"> this.minimumStepSize = conf.getFloat(MIN_STEP_KEY, DEFAULT_MIN_STEP_VALUE);</span>
<span class="source-line-no">405</span><span id="line-405"> this.step = this.maximumStepSize;</span>
<span class="source-line-no">406</span><span id="line-406"> this.sufficientMemoryLevel =</span>
<span class="source-line-no">407</span><span id="line-407"> conf.getFloat(SUFFICIENT_MEMORY_LEVEL_KEY, DEFAULT_SUFFICIENT_MEMORY_LEVEL_VALUE);</span>
<span class="source-line-no">408</span><span id="line-408"> this.tunerLookupPeriods = conf.getInt(LOOKUP_PERIODS_KEY, DEFAULT_LOOKUP_PERIODS);</span>
<span class="source-line-no">409</span><span id="line-409"> this.blockCachePercentMinRange = conf.getFloat(BLOCK_CACHE_SIZE_MIN_RANGE_KEY,</span>
<span class="source-line-no">410</span><span id="line-410"> conf.getFloat(HFILE_BLOCK_CACHE_SIZE_KEY, HConstants.HFILE_BLOCK_CACHE_SIZE_DEFAULT));</span>
<span class="source-line-no">411</span><span id="line-411"> this.blockCachePercentMaxRange = conf.getFloat(BLOCK_CACHE_SIZE_MAX_RANGE_KEY,</span>
<span class="source-line-no">412</span><span id="line-412"> conf.getFloat(HFILE_BLOCK_CACHE_SIZE_KEY, HConstants.HFILE_BLOCK_CACHE_SIZE_DEFAULT));</span>
<span class="source-line-no">413</span><span id="line-413"> this.globalMemStorePercentMinRange = conf.getFloat(MEMSTORE_SIZE_MIN_RANGE_KEY,</span>
<span class="source-line-no">414</span><span id="line-414"> MemorySizeUtil.getGlobalMemStoreHeapPercent(conf, false));</span>
<span class="source-line-no">415</span><span id="line-415"> this.globalMemStorePercentMaxRange = conf.getFloat(MEMSTORE_SIZE_MAX_RANGE_KEY,</span>
<span class="source-line-no">416</span><span id="line-416"> MemorySizeUtil.getGlobalMemStoreHeapPercent(conf, false));</span>
<span class="source-line-no">417</span><span id="line-417"> this.globalMemStoreLimitLowMarkPercent =</span>
<span class="source-line-no">418</span><span id="line-418"> MemorySizeUtil.getGlobalMemStoreHeapLowerMark(conf, true);</span>
<span class="source-line-no">419</span><span id="line-419"> // Default value of periods to ignore is number of lookup periods</span>
<span class="source-line-no">420</span><span id="line-420"> this.numPeriodsToIgnore = conf.getInt(NUM_PERIODS_TO_IGNORE, this.tunerLookupPeriods);</span>
<span class="source-line-no">421</span><span id="line-421"> this.rollingStatsForCacheMisses = new RollingStatCalculator(this.tunerLookupPeriods);</span>
<span class="source-line-no">422</span><span id="line-422"> this.rollingStatsForFlushes = new RollingStatCalculator(this.tunerLookupPeriods);</span>
<span class="source-line-no">423</span><span id="line-423"> this.rollingStatsForEvictions = new RollingStatCalculator(this.tunerLookupPeriods);</span>
<span class="source-line-no">424</span><span id="line-424"> this.rollingStatsForTunerSteps = new RollingStatCalculator(this.tunerLookupPeriods);</span>
<span class="source-line-no">425</span><span id="line-425"> }</span>
<span class="source-line-no">426</span><span id="line-426"></span>
<span class="source-line-no">427</span><span id="line-427"> private enum StepDirection {</span>
<span class="source-line-no">428</span><span id="line-428"> // block cache size was increased</span>
<span class="source-line-no">429</span><span id="line-429"> INCREASE_BLOCK_CACHE_SIZE,</span>
<span class="source-line-no">430</span><span id="line-430"> // memstore size was increased</span>
<span class="source-line-no">431</span><span id="line-431"> INCREASE_MEMSTORE_SIZE,</span>
<span class="source-line-no">432</span><span id="line-432"> // no operation was performed</span>
<span class="source-line-no">433</span><span id="line-433"> NEUTRAL</span>
<span class="source-line-no">434</span><span id="line-434"> }</span>
<span class="source-line-no">435</span><span id="line-435">}</span>
</pre>
</div>
</main>
</body>
</html>