|
@@ -21,6 +21,8 @@ package org.apache.hadoop.mapred;
|
|
import java.io.FileDescriptor;
|
|
import java.io.FileDescriptor;
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
import java.io.RandomAccessFile;
|
|
import java.io.RandomAccessFile;
|
|
|
|
+import java.nio.ByteBuffer;
|
|
|
|
+import java.nio.channels.FileChannel;
|
|
import java.nio.channels.WritableByteChannel;
|
|
import java.nio.channels.WritableByteChannel;
|
|
|
|
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.Log;
|
|
@@ -30,6 +32,8 @@ import org.apache.hadoop.io.ReadaheadPool.ReadaheadRequest;
|
|
import org.apache.hadoop.io.nativeio.NativeIO;
|
|
import org.apache.hadoop.io.nativeio.NativeIO;
|
|
import org.jboss.netty.channel.DefaultFileRegion;
|
|
import org.jboss.netty.channel.DefaultFileRegion;
|
|
|
|
|
|
|
|
+import com.google.common.annotations.VisibleForTesting;
|
|
|
|
+
|
|
public class FadvisedFileRegion extends DefaultFileRegion {
|
|
public class FadvisedFileRegion extends DefaultFileRegion {
|
|
|
|
|
|
private static final Log LOG = LogFactory.getLog(FadvisedFileRegion.class);
|
|
private static final Log LOG = LogFactory.getLog(FadvisedFileRegion.class);
|
|
@@ -39,18 +43,29 @@ public class FadvisedFileRegion extends DefaultFileRegion {
|
|
private final ReadaheadPool readaheadPool;
|
|
private final ReadaheadPool readaheadPool;
|
|
private final FileDescriptor fd;
|
|
private final FileDescriptor fd;
|
|
private final String identifier;
|
|
private final String identifier;
|
|
-
|
|
|
|
|
|
+ private final long count;
|
|
|
|
+ private final long position;
|
|
|
|
+ private final int shuffleBufferSize;
|
|
|
|
+ private final boolean shuffleTransferToAllowed;
|
|
|
|
+ private final FileChannel fileChannel;
|
|
|
|
+
|
|
private ReadaheadRequest readaheadRequest;
|
|
private ReadaheadRequest readaheadRequest;
|
|
|
|
|
|
public FadvisedFileRegion(RandomAccessFile file, long position, long count,
|
|
public FadvisedFileRegion(RandomAccessFile file, long position, long count,
|
|
boolean manageOsCache, int readaheadLength, ReadaheadPool readaheadPool,
|
|
boolean manageOsCache, int readaheadLength, ReadaheadPool readaheadPool,
|
|
- String identifier) throws IOException {
|
|
|
|
|
|
+ String identifier, int shuffleBufferSize,
|
|
|
|
+ boolean shuffleTransferToAllowed) throws IOException {
|
|
super(file.getChannel(), position, count);
|
|
super(file.getChannel(), position, count);
|
|
this.manageOsCache = manageOsCache;
|
|
this.manageOsCache = manageOsCache;
|
|
this.readaheadLength = readaheadLength;
|
|
this.readaheadLength = readaheadLength;
|
|
this.readaheadPool = readaheadPool;
|
|
this.readaheadPool = readaheadPool;
|
|
this.fd = file.getFD();
|
|
this.fd = file.getFD();
|
|
this.identifier = identifier;
|
|
this.identifier = identifier;
|
|
|
|
+ this.fileChannel = file.getChannel();
|
|
|
|
+ this.count = count;
|
|
|
|
+ this.position = position;
|
|
|
|
+ this.shuffleBufferSize = shuffleBufferSize;
|
|
|
|
+ this.shuffleTransferToAllowed = shuffleTransferToAllowed;
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -61,9 +76,69 @@ public class FadvisedFileRegion extends DefaultFileRegion {
|
|
getPosition() + position, readaheadLength,
|
|
getPosition() + position, readaheadLength,
|
|
getPosition() + getCount(), readaheadRequest);
|
|
getPosition() + getCount(), readaheadRequest);
|
|
}
|
|
}
|
|
- return super.transferTo(target, position);
|
|
|
|
|
|
+
|
|
|
|
+ if(this.shuffleTransferToAllowed) {
|
|
|
|
+ return super.transferTo(target, position);
|
|
|
|
+ } else {
|
|
|
|
+ return customShuffleTransfer(target, position);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * This method transfers data using local buffer. It transfers data from
|
|
|
|
+ * a disk to a local buffer in memory, and then it transfers data from the
|
|
|
|
+ * buffer to the target. This is used only if transferTo is disallowed in
|
|
|
|
+ * the configuration file. super.TransferTo does not perform well on Windows
|
|
|
|
+ * due to a small IO request generated. customShuffleTransfer can control
|
|
|
|
+ * the size of the IO requests by changing the size of the intermediate
|
|
|
|
+ * buffer.
|
|
|
|
+ */
|
|
|
|
+ @VisibleForTesting
|
|
|
|
+ long customShuffleTransfer(WritableByteChannel target, long position)
|
|
|
|
+ throws IOException {
|
|
|
|
+ long actualCount = this.count - position;
|
|
|
|
+ if (actualCount < 0 || position < 0) {
|
|
|
|
+ throw new IllegalArgumentException(
|
|
|
|
+ "position out of range: " + position +
|
|
|
|
+ " (expected: 0 - " + (this.count - 1) + ')');
|
|
|
|
+ }
|
|
|
|
+ if (actualCount == 0) {
|
|
|
|
+ return 0L;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ long trans = actualCount;
|
|
|
|
+ int readSize;
|
|
|
|
+ ByteBuffer byteBuffer = ByteBuffer.allocate(this.shuffleBufferSize);
|
|
|
|
+
|
|
|
|
+ while(trans > 0L &&
|
|
|
|
+ (readSize = fileChannel.read(byteBuffer, this.position+position)) > 0) {
|
|
|
|
+ //adjust counters and buffer limit
|
|
|
|
+ if(readSize < trans) {
|
|
|
|
+ trans -= readSize;
|
|
|
|
+ position += readSize;
|
|
|
|
+ byteBuffer.flip();
|
|
|
|
+ } else {
|
|
|
|
+ //We can read more than we need if the actualCount is not multiple
|
|
|
|
+ //of the byteBuffer size and file is big enough. In that case we cannot
|
|
|
|
+ //use flip method but we need to set buffer limit manually to trans.
|
|
|
|
+ byteBuffer.limit((int)trans);
|
|
|
|
+ byteBuffer.position(0);
|
|
|
|
+ position += trans;
|
|
|
|
+ trans = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //write data to the target
|
|
|
|
+ while(byteBuffer.hasRemaining()) {
|
|
|
|
+ target.write(byteBuffer);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ byteBuffer.clear();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return actualCount - trans;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
@Override
|
|
@Override
|
|
public void releaseExternalResources() {
|
|
public void releaseExternalResources() {
|
|
if (readaheadRequest != null) {
|
|
if (readaheadRequest != null) {
|