MASA-Core
AbstractDiagonalAligner.cpp
Go to the documentation of this file.
00001 /*******************************************************************************
00002  *
00003  * Copyright (c) 2010-2015   Edans Sandes
00004  *
00005  * This file is part of MASA-Core.
00006  * 
00007  * MASA-Core is free software: you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation, either version 3 of the License, or
00010  * (at your option) any later version.
00011  * 
00012  * MASA-Core is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU General Public License
00018  * along with MASA-Core.  If not, see <http://www.gnu.org/licenses/>.
00019  *
00020  ******************************************************************************/
00021 
00022 #include "AbstractDiagonalAligner.hpp"
00023 
00024 #include <stdlib.h>
00025 
00026 /**
00027  * Set to (1) in order to print debug information in the stdout. This
00028  * significantly degrades the performance.
00029  */
00030 #define DEBUG (0)
00031 
00032 /**
00033  * Defines a lower-bound flush interval between two special rows.
00034  */
00035 #define MINIMUM_FLUSH_INTERVAL  (8*1024)
00036 
00037 /**
00038  * AbstractDiagonalAligner constructor.
00039  */
00040 AbstractDiagonalAligner::AbstractDiagonalAligner() {
00041         pruner = new BlockPruningDiagonal();
00042 
00043 }
00044 
00045 /**
00046  * AbstractDiagonalAligner destructor.
00047  */
00048 AbstractDiagonalAligner::~AbstractDiagonalAligner() {
00049 
00050 }
00051 
00052 
00053 /**
00054  * Aligns the given partition, processing it by antidiagonals.
00055  *
00056  * @param partition partition to be aligned.
00057  * @see IAligner::alignPartition
00058  */
00059 void AbstractDiagonalAligner::alignPartition(Partition partition) {
00060         this->partition = partition;
00061 
00062         //createDispatcherQueue();
00063         prepareIterations();
00064         while (hasMoreIterations() && mustContinue()) {
00065                 processNextIteration();
00066         }
00067         finalizeIterations();
00068         if (DEBUG) printf("End of alignPartition: %d %d\n", hasMoreIterations(), mustContinue());
00069         //destroyDispatcherQueue();
00070 }
00071 
00072 /**
00073  * Configures the grid and initializes other structures before the beginning of
00074  * the diagonal iterations.
00075  */
00076 void AbstractDiagonalAligner::prepareIterations() {
00077         Grid* grid = configureGrid(partition);
00078 
00079         initializeBlockPruning(pruner);
00080 
00081         h_loadColumn = (cell_t*)malloc((getBlockHeight()+1)*sizeof(cell_t));
00082 
00083         /* reads the first top-left cell of the partition. */
00084         cell_t dummy;
00085         receiveFirstColumn(&dummy, 1); // initializes the AbstractAligner::firstColumnTail
00086         receiveFirstRow(&dummy, 1); // initializes the AbstractAligner::firstRowTail
00087         // TODO assert if both tails are equal?
00088 
00089         loadFirstRow();
00090 
00091 
00092         currentExternalDiagonal = 0;
00093 
00094         grid->getGridHeight();
00095         gridHeight = partition.getHeight()/getBlockHeight() + 1;
00096     externalDiagonalCount = gridHeight + gridWidth;
00097     if (DEBUG) printf("Grid height: %d    Blocks: %d    %d-%d:%d\n", gridHeight, gridWidth, partition.getI1(), partition.getI0(), partition.getHeight());
00098     if (DEBUG) printf("START: %d steps\n", externalDiagonalCount);
00099 
00100         windowStart = 0;
00101         windowEnd = gridWidth;
00102 
00103     initializeDiagonals(); // calls subclass
00104 }
00105 
00106 /**
00107  * Processes the next diagonal and loads/flushes the rows and columns necessary
00108  * for the MASA-Core integration.
00109  */
00110 void AbstractDiagonalAligner::processNextIteration() {
00111 
00112         /* Implemented capability: aligner_capabilities_t::customize_first_column */
00113         if (getFirstColumnInitType() != INIT_WITH_ZEROES) {
00114                 loadFirstColumn();
00115         }
00116 
00117         processDiagonal(currentExternalDiagonal, windowStart, windowEnd);
00118 
00119         /* Implemented aligner_capabilities_t::dispatch_special_row */
00120         if (mustDispatchSpecialRows()) {
00121                 flushSpecialRows();
00122         }
00123         /* Implemented aligner_capabilities_t::dispatch_last_row */
00124         if (mustDispatchLastRow()) {
00125                 flushLastRow();
00126         }
00127         /* Implemented aligner_capabilities_t::dispatch_last_rcolumn */
00128         if (mustDispatchLastColumn()) {
00129                 flushLastColumn();
00130         }
00131         /* Implemented aligner_capabilities_t::dispatch_scores */
00132         if (mustDispatchScores()) {
00133                 flushBlockScores();
00134         }
00135         /* Implemented aligner_capabilities_t::block_pruning */
00136         if (mustPruneBlocks()) {
00137                 pruneBlocks();
00138         }
00139         /* Implemented aligner_capabilities_t::dispatch_last_cell */
00140         if (mustDispatchLastCell()) {
00141                 flushLastCell();
00142         }
00143 
00144         /* Statistics */
00145         int b0 = max(0, currentExternalDiagonal - (externalDiagonalCount - gridWidth));
00146         int b1 = min(currentExternalDiagonal, gridWidth);
00147         int jb0;
00148         int jb1;
00149         statTotalBlocks += b1 - b0 + 1;
00150         statPrunedBlocksLeft += max(windowStart - b0, 0);
00151         statPrunedBlocksRight += max(b1 - windowEnd - 1, 0);
00152         getGrid()->getBlockPosition(b0  , 0, NULL, &jb0, NULL, NULL);
00153         getGrid()->getBlockPosition(b1-1, 0, NULL, NULL, NULL, &jb1);
00154         if (jb1 > 0) {
00155                 statTotalCells += ((long long)jb1-jb0)* getBlockHeight();
00156         }
00157 
00158         currentExternalDiagonal++;
00159 }
00160 
00161 /**
00162  * Tests if we have more diagonals to be processed.
00163  * @return true if we have more diagonals.
00164  */
00165 bool AbstractDiagonalAligner::hasMoreIterations() {
00166         return currentExternalDiagonal < externalDiagonalCount;
00167 }
00168 
00169 /**
00170  * Finalizes the execution of the partition.
00171  */
00172 void AbstractDiagonalAligner::finalizeIterations() {
00173         finalizeDiagonals(); // calls subclass
00174 
00175         delete h_loadColumn;
00176         h_loadColumn = NULL;
00177 }
00178 
00179 /**
00180  * Configures the grid for the given partition.
00181  * @param partition partition to be aligned.
00182  */
00183 Grid* AbstractDiagonalAligner::configureGrid(Partition partition) {
00184         /* creates the grid */
00185         Grid* grid = this->createGrid(partition);
00186 
00187         this->gridWidth = getGridWidth(partition.getWidth());
00188         grid->setBlockHeight(getBlockHeight());
00189         grid->splitGridHorizontally(gridWidth);
00190 
00191         /* grid statistics */
00192         statMinGridWidth  = std::min(statMinGridWidth , gridWidth);
00193         statMaxGridWidth  = std::max(statMaxGridWidth , gridWidth);
00194 
00195         if (DEBUG) printf("Configured Grid width: %d\n", gridWidth);
00196         return grid;
00197 }
00198 
00199 
00200 /**
00201  * @copydoc IAligner::clearStatistics
00202  */
00203 void AbstractDiagonalAligner::clearStatistics() {
00204         statPrunedBlocksLeft = 0;
00205         statPrunedBlocksRight = 0;
00206         statTotalBlocks = 0;
00207         statTotalCells = 0;
00208 
00209         statMinGridWidth = INF;
00210         statMaxGridWidth = 0;
00211 }
00212 
00213 /** Empty stub for the superclass virtual method.
00214  * @copydoc IAligner::clearStatistics
00215  */
00216 void AbstractDiagonalAligner::printInitialStatistics(FILE* file) {
00217 }
00218 
00219 /** Empty stub for the superclass virtual method.
00220  * @copydoc IAligner::printStageStatistics
00221  */
00222 void AbstractDiagonalAligner::printStageStatistics(FILE* file) {
00223 }
00224 
00225 /** Empty stub for the superclass virtual method.
00226  * @copydoc IAligner::printFinalStatistics
00227  */
00228 void AbstractDiagonalAligner::printFinalStatistics(FILE* file) {
00229 }
00230 
00231 /**
00232  * Prints the pruning statistics and grid width used range.
00233  * @param file handler to print out the statistics.
00234  * @see IAligner::printStatistics
00235  */
00236 void AbstractDiagonalAligner::printStatistics(FILE* file) {
00237 
00238         fprintf(file, "\n=====  PRUNING STATS   =====\n");
00239         fprintf(file, "Pruned Blocks: %d = %d + %d\n",
00240                         statPrunedBlocksLeft+statPrunedBlocksRight,
00241                         statPrunedBlocksLeft,
00242                         statPrunedBlocksRight);
00243         fprintf(file, "Pruned Blocks: %.4f = %.4f + %.4f\n",
00244                         ((statPrunedBlocksLeft+statPrunedBlocksRight) * 100.0f) / statTotalBlocks,
00245                         (statPrunedBlocksLeft * 100.0f) / statTotalBlocks,
00246                         (statPrunedBlocksRight * 100.0f) / statTotalBlocks);
00247 
00248         fprintf(file, "\n===== RUNTIME VARIABLES =====\n");
00249         fprintf(file, "     Block Count: %d-%d\n", statMinGridWidth, statMaxGridWidth);
00250 
00251         fflush(file);
00252 }
00253 
00254 /**
00255  * Returns the number of processed cells since the last call to
00256  * AbstractDiagonalAligner::clearStatistics method.
00257  *
00258  * @return number of processed cells
00259  */
00260 long long AbstractDiagonalAligner::getProcessedCells() {
00261         return statTotalCells;
00262 }
00263 
00264 /**
00265  * MASA-Core prints this string periodically.
00266  * @return the string to be printed.
00267  */
00268 const char* AbstractDiagonalAligner::getProgressString() const {
00269         static char str[128];
00270 
00271         sprintf(str, "PROGRESS: %4d/%4d  cut:%d/%d (%d/%d %.1f%% %.1f%%)",
00272                         currentExternalDiagonal, externalDiagonalCount,
00273                         windowStart, windowEnd,
00274                         this->statPrunedBlocksLeft, this->statPrunedBlocksRight,
00275                         (this->statPrunedBlocksLeft * 100.0f) / statTotalBlocks,
00276                 (this->statPrunedBlocksRight * 100.0f) / statTotalBlocks);
00277         return str;
00278 }
00279 
00280 /**
00281  * Iterates on the blocks and check if any of them must flush its last row
00282  * in the disk. If so, the row is copied from the aligner and dispatched to the
00283  * MASA-Core. Note that the blocks are disposed in a diagonal, so there may
00284  * be a maximum of one block per special row.
00285  */
00286 void AbstractDiagonalAligner::flushSpecialRows() {
00287         if (getSpecialRowInterval() > 0) {
00288                 const int blockHeight = getBlockHeight();
00289 
00290         if (isSpecialRow(currentExternalDiagonal+1)) {
00291                 /*
00292                  * Dispatches the first cell of the row, that is copied from
00293                  * the first column (firstColumnTail).
00294                  */
00295                 cell_t first_cell = getFirstColumnTail();
00296                 first_cell.f = -INF;
00297                 dispatchRow(partition.getI0()+(currentExternalDiagonal+1)*blockHeight, &first_cell, 1);
00298         }
00299 
00300                 for (int k = 0; k < gridWidth && k <= currentExternalDiagonal; k++) {
00301                         int bx = k;
00302                         int by = currentExternalDiagonal - bx;
00303 
00304                         if (isSpecialRow(by)) { // Check if this block must be flushed
00305                                 int i = by * blockHeight;
00306                                 int x0;
00307                                 int x1;
00308                                 getGrid()->getBlockPosition(bx, 0, NULL, &x0, NULL, &x1);
00309                                 int xLen = x1 - x0;
00310 
00311                                 const cell_t* specialRow = getSpecialRow(x0, xLen); // from subclass
00312                                 dispatchRow(partition.getI0() + i, specialRow, xLen);
00313                         }
00314 
00315                 }
00316         }
00317 }
00318 
00319 /**
00320  * This function reads a chunk of the last row from the aligner and dispatch
00321  * it to the MASA-Core. This method is called multiple times during the
00322  * last external diagonals, since each of the bottom-most blocks
00323  * calculates a range of the last row.
00324  */
00325 void AbstractDiagonalAligner::flushLastRow() {
00326         if (currentExternalDiagonal >= gridHeight-1) {
00327 
00328                 for (int k = 1; k <= gridWidth && k <= currentExternalDiagonal; k++) {
00329                         int bx = k;
00330                         const int by = currentExternalDiagonal - bx;
00331 
00332                         bool flushThisRow = (by == gridHeight-1);
00333 
00334                         if (flushThisRow) {
00335                                 int x0;
00336                                 int x1;
00337                                 getGrid()->getBlockPosition(bx-1, 0, NULL, &x0, NULL, &x1);
00338                                 int xLen = x1 - x0;
00339                                 const cell_t* lastRow = getLastRow(x0, xLen); // from subclass
00340                                 if (DEBUG) printf("dispatchRow: %d..%d %d %d\n", x0, x1, xLen, bx);
00341                                 if (bx == 1) {
00342                                         cell_t first_cell = getFirstColumnTail();
00343                                         first_cell.f = -INF;
00344                                 dispatchRow(partition.getI1(), &first_cell, 1);
00345                                 }
00346                                 dispatchRow(partition.getI1(), lastRow, xLen);
00347                         }
00348 
00349                 }
00350         }
00351 }
00352 
00353 /**
00354  * This function reads a chunk of the last column from the aligner and dispatch
00355  * it to the MASA-Core. This method is called once for each external diagonal,
00356  * since the last block is always calculating a new chunk of the last column.
00357  */
00358 void AbstractDiagonalAligner::flushLastColumn() {
00359         const int blockHeight = getBlockHeight();
00360         int i = partition.getI0() + (currentExternalDiagonal-gridWidth)*blockHeight;
00361         if (i >= partition.getI0()) {
00362                 int len = getBlockHeight();
00363                 if (i+len > partition.getI1()) {
00364                         len = partition.getI1()-i;
00365                 }
00366                 const cell_t* column_chunk = getLastColumn(i, len); // from subclass
00367                 dispatchColumn(partition.getJ1(), column_chunk, len);
00368         }
00369 }
00370 
00371 /**
00372  * This function reads the bottom-left cell from aligner and dispatch it
00373  * to the MASA-Core.
00374  */
00375 void AbstractDiagonalAligner::flushLastCell() {
00376         if (currentExternalDiagonal+1 >= externalDiagonalCount) { // last iteration
00377                 const cell_t* lastCell = getLastRow(partition.getJ1()-1, 1); // from subclass
00378 
00379                 score_t score;
00380                 score.i = partition.getI1()-1;
00381                 score.j = partition.getJ1()-1;
00382                 score.score = lastCell->h;
00383 
00384                 dispatchScore(score);
00385         }
00386 }
00387 
00388 /**
00389  * This function reads the block scores from aligner and dispatch them
00390  * to the MASA-Core.
00391  */
00392 void AbstractDiagonalAligner::flushBlockScores() {
00393         const score_t* scores = getBlockScores(); // from subclass
00394         for (int bl=0; bl<gridWidth; bl++) {
00395                 int x = bl;
00396                 int y = currentExternalDiagonal - bl;
00397 
00398                 if (y >= 0 && y < externalDiagonalCount) {
00399                         // Dispatch scores for each valid block
00400                         dispatchScore(scores[bl], x, y);
00401                 }
00402         }
00403 }
00404 
00405 /**
00406  * Initialize the first row of the aligner with the cells received
00407  * from the MASA-Core. The first row is read all at once.
00408  */
00409 void AbstractDiagonalAligner::loadFirstRow() {
00410         // The seq1_offset allow us to reduce the memory usage.
00411         int j0 = partition.getJ0();
00412         int j1 = partition.getJ1();
00413 
00414         int chunk_size = j1+10; // TODO optimize
00415         cell_t* chunk = (cell_t*)malloc(chunk_size*sizeof(cell_t));
00416 
00417         receiveFirstRow((cell_t*) ((chunk + j0)), j1 - j0); // from MASA-Core
00418 
00419         // The 1st cell of the last column is the last cell of the first row.
00420         cell_t first_cell = getFirstRowTail();
00421         first_cell.f = -INF;
00422         dispatchColumn(partition.getJ1(), &first_cell, 1);
00423 
00424         setFirstRow((const cell_t*)&chunk[j0], j0, j1-j0); // to subclass
00425         delete chunk;
00426 }
00427 
00428 /**
00429  * Initialize the first column of the aligner with the cells received
00430  * from the MASA-Core. The first column is read in
00431  * chunks, which size is defined by the getBlockHeight() function.
00432  */
00433 void AbstractDiagonalAligner::loadFirstColumn() {
00434         int pos_i = ((currentExternalDiagonal)*getBlockHeight());
00435     if (pos_i < partition.getHeight()) {
00436         int len = getBlockHeight(); // from subclass
00437         if (pos_i + len >= partition.getHeight()) {
00438                 len = partition.getHeight() - pos_i;
00439         }
00440 
00441         /* Puts the H[i-1][j-1] dependency in the first cell of the column. */
00442         h_loadColumn[0] = getFirstColumnTail();
00443 
00444         /* Receives the first column chunk from the MASA-core */
00445         receiveFirstColumn((cell_t*) (h_loadColumn+1), len); // from MASA-Core
00446 
00447                 /* Padding */
00448                 for (int i = len; i < getBlockHeight(); i++) {
00449                         h_loadColumn[i].h = -INF;
00450                         h_loadColumn[i].e = -INF;
00451                 }
00452 
00453                 /* Updates the first column in GPU */
00454                 setFirstColumn(h_loadColumn, pos_i, len); // to subclass
00455     }
00456 }
00457 
00458 
00459 /**
00460  * Defines if the blocks in row $by$ must flush their last row. The top-most
00461  * and bottom-most rows are never special rows.
00462  *
00463  * @param by the block's row id
00464  * @return true if the blocks inr row $by$ must save their last row.
00465  */
00466 bool AbstractDiagonalAligner::isSpecialRow(int by) {
00467         const int block_height = getBlockHeight();
00468         int flush_block_interval = (getSpecialRowInterval()+block_height-1)/block_height;
00469         if (flush_block_interval <= 0) {
00470                 flush_block_interval = 1;
00471         }
00472         if (flush_block_interval <= MINIMUM_FLUSH_INTERVAL/block_height) {
00473                 flush_block_interval = MINIMUM_FLUSH_INTERVAL/block_height;
00474         }
00475 
00476         int i = by * block_height;
00477         return ((by % flush_block_interval == 0) && (i > 0 && i < partition.getHeight()) );
00478 }
00479 
00480 /**
00481  * Returns the partition currently being processed.
00482  * @return the partition currently being processed.
00483  */
00484 Partition AbstractDiagonalAligner::getPartition() const {
00485         return partition;
00486 }
00487 
00488 /**
00489  * Updates the pruning window accordingly to the last block scores.
00490  */
00491 void AbstractDiagonalAligner::pruneBlocks() {
00492         const score_t* block_scores = getBlockScores();
00493         int prevStart;
00494         int prevEnd;
00495         pruner->getNonPrunableWindow(&prevStart, &prevEnd);
00496         pruner->updatePruningWindow(currentExternalDiagonal-1, block_scores);
00497         pruner->getNonPrunableWindow(&windowStart, &windowEnd);
00498         if (windowEnd < prevEnd) {
00499                 clearPrunedBlocks(windowEnd, prevEnd);
00500         }
00501 }
00502 
00503 
00504