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

eu.stratosphere.pact.runtime.hash.InMemoryPartition Maven / Gradle / Ivy

There is a newer version: 0.5.2-hadoop2
Show newest version
/***********************************************************************************************************************
 * Copyright (C) 2010-2013 by the Stratosphere project (http://stratosphere.eu)
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 **********************************************************************************************************************/

package eu.stratosphere.pact.runtime.hash;

import java.io.EOFException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import eu.stratosphere.api.common.typeutils.TypeSerializer;
import eu.stratosphere.core.memory.MemorySegment;
import eu.stratosphere.core.memory.MemorySegmentSource;
import eu.stratosphere.core.memory.SeekableDataInputView;
import eu.stratosphere.nephele.services.memorymanager.AbstractPagedInputView;
import eu.stratosphere.nephele.services.memorymanager.AbstractPagedOutputView;
import eu.stratosphere.nephele.services.memorymanager.ListMemorySegmentSource;

/**
 * In-memory partition with overflow buckets for {@link CompactingHashTable}
 * 
 * @param T record type
 */
public class InMemoryPartition {
	
	// --------------------------------- Table Structure Auxiliaries ------------------------------------
	
	protected MemorySegment[] overflowSegments;	// segments in which overflow buckets from the table structure are stored
	
	protected int numOverflowSegments;			// the number of actual segments in the overflowSegments array
	
	protected int nextOverflowBucket;				// the next free bucket in the current overflow segment

	// -------------------------------------  Type Accessors --------------------------------------------
	
	private final TypeSerializer serializer;
	
	// -------------------------------------- Record Buffers --------------------------------------------
	
	private final ArrayList partitionPages;
	
	private final ListMemorySegmentSource availableMemory;
	
	private final WriteView writeView;
	
	private final ReadView readView;
	
	private long recordCounter;				// number of records in this partition including garbage
	
	// ----------------------------------------- General ------------------------------------------------
	
	private int partitionNumber;					// the number of the partition
	
	private boolean compacted;						// overwritten records since allocation or last full compaction
	
	// --------------------------------------------------------------------------------------------------
	
	
	
	/**
	 * Creates a new partition, in memory, with one buffer. 
	 * 
	 * @param serializer Serializer for T.
	 * @param partitionNumber The number of the partition.
	 * @param memSource memory pool
	 * @param pageSize segment size in bytes
	 * @param pageSizeInBits
	 */
	public InMemoryPartition(TypeSerializer serializer, int partitionNumber,
			ListMemorySegmentSource memSource, int pageSize, int pageSizeInBits)
	{
		this.overflowSegments = new MemorySegment[2];
		this.numOverflowSegments = 0;
		this.nextOverflowBucket = 0;
		
		this.serializer = serializer;
		this.partitionPages = new ArrayList(64);
		this.availableMemory = memSource;
		
		this.partitionNumber = partitionNumber;
		
		// add the first segment
		this.partitionPages.add(memSource.nextSegment());
		// empty partitions have no garbage
		this.compacted = true;
		
		this.writeView = new WriteView(this.partitionPages, memSource, pageSize, pageSizeInBits);
		this.readView = new ReadView(this.partitionPages, pageSize, pageSizeInBits);
	}
	
	// --------------------------------------------------------------------------------------------------
	
	/**
	 * Gets the partition number of this partition.
	 * 
	 * @return This partition's number.
	 */
	public int getPartitionNumber() {
		return this.partitionNumber;
	}
	
	/**
	 * overwrites partition number and should only be used on compaction partition
	 * @param number new partition
	 */
	public void setPartitionNumber(int number) {
		this.partitionNumber = number;
	}
	
	/**
	 * 
	 * @return number of segments owned by partition
	 */
	public int getBlockCount() {
		return this.partitionPages.size();
	}
	
	/**
	 * number of records in partition including garbage
	 * 
	 * @return number record count
	 */
	public long getRecordCount() {
		return this.recordCounter;
	}
	
	/**
	 * sets record counter to zero and should only be used on compaction partition
	 */
	public void resetRecordCounter() {
		this.recordCounter = 0L;
	}
	
	/**
	 * @return true if garbage exists in partition
	 */
	public boolean isCompacted() {
		return this.compacted;
	}
	
	/**
	 * sets compaction status (should only be set true directly after compaction and false when garbage was created)
	 * 
	 * @param compacted compaction status
	 */
	public void setCompaction(boolean compacted) {
		this.compacted = compacted;
	}
	
	// --------------------------------------------------------------------------------------------------
	
	/**
	 * Inserts the given object into the current buffer. This method returns a pointer that
	 * can be used to address the written record in this partition.
	 * 
	 * @param record The object to be written to the partition.
	 * @return A pointer to the object in the partition.
	 * @throws IOException Thrown when the write failed.
	 */
	public final long appendRecord(T record) throws IOException {
		long pointer = this.writeView.getCurrentPointer();
		try {
			this.serializer.serialize(record, this.writeView);
			this.recordCounter++;
			return pointer;
		}
		catch (EOFException e) {
			// we ran out of pages. 
			// first, reset the pages and then we need to trigger a compaction
			//int oldCurrentBuffer = 
			this.writeView.resetTo(pointer);
			//for (int bufNum = this.partitionPages.size() - 1; bufNum > oldCurrentBuffer; bufNum--) {
			//	this.availableMemory.addMemorySegment(this.partitionPages.remove(bufNum));
			//}
			throw e;
		}
	}
	
	public void readRecordAt(long pointer, T record) throws IOException {
		this.readView.setReadPosition(pointer);
		this.serializer.deserialize(record, this.readView);
	}
	
	/**
	 * UNSAFE!! overwrites record
	 * causes inconsistency or data loss for overwriting everything but records of the exact same size
	 * 
	 * @param pointer pointer to start of record
	 * @param record record to overwrite old one with
	 * @throws IOException
	 */
	@Deprecated
	public void overwriteRecordAt(long pointer, T record) throws IOException {
		long tmpPointer = this.writeView.getCurrentPointer();
		this.writeView.resetTo(pointer);
		this.serializer.serialize(record, this.writeView);
		this.writeView.resetTo(tmpPointer);
	}
	
	/**
	 * releases all of the partition's segments (pages and overflow buckets)
	 * 
	 * @param target memory pool to release segments to
	 */
	public void clearAllMemory(List target) {
		// return the overflow segments
		if (this.overflowSegments != null) {
			for (int k = 0; k < this.numOverflowSegments; k++) {
				target.add(this.overflowSegments[k]);
			}
		}
		
		// return the partition buffers
		target.addAll(this.partitionPages);
		this.partitionPages.clear();
	}
	
	/**
	 * attempts to allocate specified number of segments and should only be used by compaction partition
	 * fails silently if not enough segments are available since next compaction could still succeed
	 * 
	 * @param numberOfSegments allocation count
	 */
	public void allocateSegments(int numberOfSegments) {
		while(getBlockCount() < numberOfSegments) {
			MemorySegment next = this.availableMemory.nextSegment();
			if(next != null) {
				this.partitionPages.add(next);
			} else {
				return;
			}
		}
	}
	
	public void releaseSegments(int maxSegmentNumber, ArrayList target) {
		while(getBlockCount() > maxSegmentNumber) {
			target.add(partitionPages.remove(partitionPages.size()-1));
		}
	}

	@Override
	public String toString() {
		return String.format("Partition %d - %d records, %d partition blocks, %d bucket overflow blocks", getPartitionNumber(), getRecordCount(), getBlockCount(), this.numOverflowSegments);
	}
	
	// ============================================================================================
	
	private static final class WriteView extends AbstractPagedOutputView {
		
		private final ArrayList pages;
		
		private final MemorySegmentSource memSource;
		
		private final int sizeBits;
		
		private final int sizeMask;
		
		private int currentPageNumber;
		
		private int segmentNumberOffset;
		
		
		private WriteView(ArrayList pages, MemorySegmentSource memSource,
				int pageSize, int pageSizeBits)
		{
			super(pages.get(0), pageSize, 0);
			
			this.pages = pages;
			this.memSource = memSource;
			this.sizeBits = pageSizeBits;
			this.sizeMask = pageSize - 1;
		}
		

		@Override
		protected MemorySegment nextSegment(MemorySegment current, int bytesUsed) throws IOException {
			MemorySegment next = this.memSource.nextSegment();
			if(next == null) {
				throw new EOFException();
			}
			this.pages.add(next);
			
			this.currentPageNumber++;
			return next;
		}
		
		private long getCurrentPointer() {
			return (((long) this.currentPageNumber) << this.sizeBits) + getCurrentPositionInSegment();
		}
		
		private int resetTo(long pointer) {
			final int pageNum  = (int) (pointer >>> this.sizeBits);
			final int offset = (int) (pointer & this.sizeMask);
			
			this.currentPageNumber = pageNum;
			
			int posInArray = pageNum - this.segmentNumberOffset;
			seekOutput(this.pages.get(posInArray), offset);
			
			return posInArray;
		}
		
		public void setSegmentNumberOffset(int offset) {
			this.segmentNumberOffset = offset;
		}
	}
	
	
	private static final class ReadView extends AbstractPagedInputView implements SeekableDataInputView {

		private final ArrayList segments;
		
		private final int segmentSizeBits;
		
		private final int segmentSizeMask;
		
		private int currentSegmentIndex;
		
		private int segmentNumberOffset;
		
		
		public ReadView(ArrayList segments, int segmentSize, int segmentSizeBits) {
			super(segments.get(0), segmentSize, 0);
			
			if ((segmentSize & (segmentSize - 1)) != 0) {
				throw new IllegalArgumentException("Segment size must be a power of 2!");
			}
			
			this.segments = segments;
			this.segmentSizeBits = segmentSizeBits;
			this.segmentSizeMask = segmentSize - 1;
		}

		@Override
		protected MemorySegment nextSegment(MemorySegment current) throws EOFException {
			if (++this.currentSegmentIndex < this.segments.size()) {
				return this.segments.get(this.currentSegmentIndex);
			} else {
				throw new EOFException();
			}
		}

		@Override
		protected int getLimitForSegment(MemorySegment segment) {
			return this.segmentSizeMask + 1;
		}
		
		@Override
		public void setReadPosition(long position) {
			final int bufferNum = ((int) (position >>> this.segmentSizeBits)) - this.segmentNumberOffset;
			final int offset = (int) (position & this.segmentSizeMask);
			
			this.currentSegmentIndex = bufferNum;
			seekInput(this.segments.get(bufferNum), offset, this.segmentSizeMask + 1);
		}
		
		public void setSegmentNumberOffset(int offset) {
			this.segmentNumberOffset = offset;
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy