All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bigdata.bop.cost.BTreeCostModel Maven / Gradle / Ivy

/**

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     [email protected]

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 * Created on Sep 30, 2010
 */
package com.bigdata.bop.cost;

import java.io.Serializable;
import java.text.NumberFormat;

import com.bigdata.btree.AbstractBTree;
import com.bigdata.btree.BTree;
import com.bigdata.journal.Journal;

/**
 * A cost model for a range scan on a {@link BTree} backed by a {@link Journal}.
 * The on disk representation of the {@link BTree} does not reflect the index
 * order so a range scan on the {@link BTree} is basically turned into one
 * random seek per node or leaf visited.
 * 
 * @author Bryan Thompson
 * @version $Id$
 * 
 * @todo Add a parameter for the write retention queue? The capacity of the
 *       queue could be turned into an estimate of the #of nodes and leaves
 *       buffered. Alternatively, we have an estimate of the #of distinct nodes
 *       and leaves on the queue in
 *       {@link AbstractBTree#ndistinctOnWriteRetentionQueue}. With that, we
 *       could decide how likely it is that the first N leaves of the
 *       {@link BTree} are in the cache. However, this is all fuzzy since a
 *       focus on one branch of the {@link BTree} could cause nothing but the
 *       root to be in the cache when probing a different branch.
 */
public class BTreeCostModel implements Serializable {

	/**
	 * @todo should be either Externalizable and explicitly managed versioning
	 *       or Serializable with a public interface for versioning.
	 */
	private static final long serialVersionUID = 1L;

	private final DiskCostModel diskCostModel;

    /**
     * 
     * @param diskCostModel
     *            The cost model for the disk on which the {@link Journal}
     *            backing the {@link BTree} is located.
     */
    public BTreeCostModel(final DiskCostModel diskCostModel) {
        
        if (diskCostModel == null)
            throw new IllegalArgumentException();
        
        this.diskCostModel = diskCostModel;
        
    }

    /**
     * Compute the height of the B+Tree from its entry count and branching
     * factor (this can also be used to find the minimum height at which there
     * could exist a given range count).
     */
    static public int estimateHeight(final int entryCount,
            final int branchingFactor) {

        if (entryCount < branchingFactor)
            return 0;

        final double logm = Math.log(branchingFactor);

        final double logn = Math.log(entryCount);
        
        final double h = (logm / logn) - 1;

        return (int) Math.ceil(h);

    }
    
    /**
     * Return the estimated cost of a range scan of the index.
     * 
     * @param rangeCount
     *            The range count for the scan.
     * @param m
     *            The B+Tree branching factor.
     * @param h
     *            The B+Tree height.
     * @param leafUtilization
     *            The leaf utilization percentage [0:100].
     * 
     * @return The estimated cost (milliseconds).
     */
    public double rangeScan(final long rangeCount, final int m, final int h,
            final int leafUtilization) {

        if (rangeCount == 0)
            return 0d;

        // average seek time to a leaf.
        final double averageSeekTime = Math.max(0, (h - 1))
                * diskCostModel.seekTime;

        // the percentage of the leaves which are full.
        // final double leafFillRate = .70d;
        final double leafFillRate = ((double) leafUtilization) / 100;

        /*
         * The expected #of leaves to visit for that range scan.
         * 
         * Note: There is an edge condition when the root leaf is empty
         * (fillRate is zero).
         */
        final double expectedLeafCount = Math.ceil((((double) rangeCount) / m)
                * Math.min(1, (1 / leafFillRate)));

        /*
         * Expected total time for the key range scan. Overestimates since
         * ignores cache reuse and OS caching of visited nodes. Ignores transfer
         * costs.
         */
        final double estimatedCost = averageSeekTime * expectedLeafCount;

        return estimatedCost;

    }

    /**
     * Prints out some tables based on different disk cost models, branching
     * factors, and range scans. To validate this, you can do a scatter plot of
     * the rangeCount and cost columns and observe the log linear curve of the
     * B+Tree.
     * 
     * @param args
     *            ignored.
     * 
     * @see query-cost-model.xls
     */
    public static void main(String[] args) {

        final DiskCostModel[] diskCostModels = new DiskCostModel[] { DiskCostModel.DEFAULT };

        final int[] branchingFactors = new int[] { 32, 64, 128, 256, 512, 1024 };

        final int[] heights = new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        
        final int[] rangeCounts = new int[] { 1, 10, 100, 1000, 2000, 5000, 10000, 2000, 50000, 100000 };

        final int leafUtilization = 65; // average percent "full" per leaf.
        
        System.out.println("seekTime\txferRate\tleafUtil\tm\theight\trangeCount\tcost(ms)");
        
        final NumberFormat millisFormat = NumberFormat.getIntegerInstance();
        millisFormat.setGroupingUsed(true);

        final NumberFormat percentFormat = NumberFormat.getPercentInstance();
        percentFormat.setMinimumFractionDigits(0);

        final StringBuilder sb = new StringBuilder();

        for (DiskCostModel diskCostModel : diskCostModels) {

            final BTreeCostModel btreeCostModel = new BTreeCostModel(
                    diskCostModel);

            for (int m : branchingFactors) {

                for (int h : heights) {

                    for (int rangeCount : rangeCounts) {

                        final int estimatedHeight = estimateHeight(rangeCount,
                                m);

                        if (estimatedHeight > h) {
                            /*
                             * Skip range counts which are too large for the
                             * current B+Tree height.
                             */
                            break;
                        }
                        
                        final double cost = btreeCostModel.rangeScan(
                                rangeCount, m, h, leafUtilization);

                        sb.setLength(0); // reset.
                        sb.append(millisFormat.format(diskCostModel.seekTime));
                        sb.append('\t');
                        sb.append(millisFormat
                                .format(diskCostModel.transferRate));
                        sb.append('\t');
                        sb.append(percentFormat.format(leafUtilization / 100d));
                        sb.append('\t');
                        sb.append(m);
                        sb.append('\t');
                        sb.append(h);
                        sb.append('\t');
                        sb.append(rangeCount);
                        sb.append('\t');
                        sb.append(millisFormat.format(cost));
                        System.out.println(sb);

                    }

                }

            }
            
        }

    }

//    private static class MockBTreeStatistics implements IBTreeStatistics {
//
//        private final int m;
//
//        private final int height;
//
//        private final long leafCount;
//
//        private final long nodeCount;
//
//        private final long entryCount;
//
//        private final IBTreeUtilizationReport utilReport;
//
//		public MockBTreeStatistics(final int m, final int height,
//				final long nodeCount, final long leafCount,
//				final long entryCount) {
//			this.m = m;
//            this.height = height;
//            this.nodeCount = nodeCount;
//            this.leafCount = leafCount;
//            this.entryCount = entryCount;
//            this.utilReport = new BTreeUtilizationReport(this);
//        }
//
//        public int getBranchingFactor() {
//            return m;
//        }
//
//        public int getHeight() {
//            return height;
//        }
//
//        public long getNodeCount() {
//            return nodeCount;
//        }
//
//        public long getLeafCount() {
//            return leafCount;
//        }
//
//        public long getEntryCount() {
//            return entryCount;
//        }
//
//        public IBTreeUtilizationReport getUtilization() {
//            return utilReport;
//        }
//
//    } // MockBTreeStatistics

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy