最近想用Hadoop实现一个A的转置乘以A的矩阵运算,假设A是100w*100的矩阵,想把100w行特征分成100个map,每个map处理1w行,每个map一次性处理1w行,而不是一行一行处理。
hadoop0.21.0这个版本已经实现了NLineInputFormat这个方法
job.setInputFormatClass(NLineInputFormat.class);
NLineInputFormat.addInputPath(job, in);
NLineInputFormat.setNumLinesPerSplit(job,10000);
这样可以实现每个map处理10000行的需求,100万行就是100个map,而不是默认的按物理块大小分配map,但是这个接口内部调用map方法的时候,仍是一行一行处理的,map方法会被执行10000次,现在改写linereader,让map方法只执行一次,每次处理10000行。
首先定义myLineInputFormat类,将job的读入方式设成myLineInputFormat。
job.setInputFormatClass(myLineInputFormat.class);
myLineInputFormat.addInputPath(job, in);
myLineInputFormat.setNumLinesPerSplit(job,10000);
myLineInputFormat的源码直接拷贝NLineInputFormat的源码,myLineInputFormat这个类里面的RecordReader返回的LongWritable, Text返回的是每个map方法里的文件偏移量和文本内容,每次读10000行,偏移量就是10000行的偏移量,text自然是10000行的文本内容。
重写myLineInputFormat类里的RecordReader方法:
public RecordReader<LongWritable, Text> createRecordReader(
InputSplit genericSplit, TaskAttemptContext context)
throws IOException {
context.setStatus(genericSplit.toString());
return new mylinereader();
}
定义myLineReader类,先源码拷贝LineRecordReader类,getFilePosition方法就是返回的每次文本的偏移量,每次的偏移量根据LineReader的readLine方法来确定,所以myLineReader的LineReader引用自己的LineReader,把import的类包改一下就成。
定义LineReader类,放在自定义的package里面,源码拷贝原先hadoop里的LineReader类,修改readLine方法:
if(appendLength > 0)
appendLength = (appendLength+1) * 10000 ;
if (appendLength > 0) {
str.append(buffer, startPosn, appendLength);
txtLength += appendLength;
}
第一个if是修改文件的长度,自己添加,第二个if是原来方法里的,如果10000行内容太多,这里append会报数组越界,所以修改LineReader的构造方法,如下,DEFAULT_BUFFER_SIZE是类属性,值也可以自己修改
public LineReader(InputStream in, Configuration conf) throws IOException {
//this(in, conf.getInt("io.file.buffer.size", DEFAULT_BUFFER_SIZE));
this(in, DEFAULT_BUFFER_SIZE);
}
同时修改readLine方法的返回值,原始是返回一行的偏移量,现在改成返回10000行的偏移量
return (int)bytesConsumed * 10000 ;