Splash-3 를 다운로드하고 컴파일하였다. Pin 을 다운로드하고 컴파일하였다.

run_benchmark_all.py 스크립트로 Splash-3 벤치마크를 실행하였다. 벤치마크 실행 옵션들을 모두 Splash-3 사이트에 제시되어 있는 recommended 옵션으로 설정하였고 쓰레드의 숫자는 4로 지정하였다. 다만 ocean과 lu의 경우에는 recommended에는 설명되어 있지 않은 또 다른 옵션이 존재하였다. 이 두 경우에 대해서 lu의 경우 Non-contiguous block allocation 방법을 사용하였고 ocean의 경우 Non-contiguous partitions 방법을 사용하였다.

두 벤치마크의 실행 방식의 차이에 대해서는 Splash의 문서 Splash-3/codes/kernals/lu/README.lu 와 Splash-3/codes/apps/ocean/README.ocean 에 나와 있다.

모든 실행에 있어서 MyPinTool.cpp로 instrumentation을 하였고 모든 instruction에 대해 operand data의 메모리 access가 존재하는 경우 그 operand의 주소와 그 주소에 대한 read 접근인지 write 접근인지를 기록하였다. 기록 파일의 형식은 UNIX용 ASCII 텍스트 파일이고 한 줄에는 다음의 정보를 기록했다.

[접근형식][공백][주소]

접근 형식은 “R” 또는 “W"의 값이고 “R"은 메모리 읽기 접근을, “W"는 메모리 쓰기 접근을 나타낸다. 공백은 한 개의 공백 문자이다. 주소는 접근한 메모리의 주소이다. 16진수로 표시되어 있고 0x를 주소의 앞에 붙여 16진수임을 나타내었다.

각 벤치마크 실행한 후 첫 100,000,000개의 메모리 접근에 대해서만 로그를 기록하고 중단하였다. 100,000,000개의 메모리 접근을 기록하는 데에는 각 벤치마크당 대략 20~30분 정도 소요되었다. 여기에는 예외가 두 개 존재하는데 fft 와 radix 이다. 이 두 벤치마크들은 100,000,000개보다 적은 수의 메모리 접근으로 실행이 끝났다. 따라서 전체 메모리 접근을 기록할 수 있었다. fft는 9,200,057번의 메모리 접근, radix는 28,596,764번의 메모리 접근으로 실행이 종료되었다. 모든 벤치마크의 실행을 종료하고 총 12개의 .log 확장자를 가지는 텍스트 파일을 생성하였다. 텍스트 파일의 이름은 각각의 벤치마크 프로그램의 이름이다.

log files

 file length    creation date     file name
-------------   --------------   -----------------
1,635,977,906    6 28 09:35        barnes.log
1,470,999,026    6 28 12:07        cholesky.log
  142,284,028    6 28 12:09        fft.log
1,529,338,037    6 28 10:00        fmm.log
1,509,959,954    6 29 09:41        lu.log
1,409,904,278    6 29 09:16        ocean.log
1,459,500,209    6 28 10:20        radiosity.log
  450,561,586    6 28 12:17        radix.log
1,685,526,320    6 28 10:37        raytrace.log
1,430,682,071    6 28 10:54        volrend.log
1,571,418,242    6 28 11:19        water-nsquared.log
1,646,977,352    6 28 11:44        water-spatial.log

이 12개의 파일을 tar로 묶고 gzip 으로 압축하여 파일 이름을 splash3_memory_access_log.tar.gz로 하였다. 이 파일의 크기는 1,422,901,777 바이트이다.

다운로드 splash3_memory_access_log.tar.gz

benchmark_run_all.py

run_benchmark_all.py 파일이다.

#!/usr/bin/env python
import  os  

benchmark_base_dir = "/Users/changp/jy/Splash-3/"
pin_base_dir = "/Users/changp/jy/pin-3.6-97554-g31f0a167d-clang-mac/"
pin_program = "pin"
pin_tool = "source/tools/MyPinTool/obj-intel64/MyPinTool.dylib"
output_dir = "/Users/changp/jy/run/"
access_limit = "100000000"

list_benchmark = [ 
    ["barnes",          "codes/apps/barnes/",                           "./BARNES < inputs/n16384-p4"],
    ["fmm",             "codes/apps/fmm/",                              "./FMM < inputs/input.4.16384"],
    ["ocean",           "codes/apps/ocean/non_contiguous_partitions",   "./OCEAN -p4 -n258"],
    ["radiosity",       "codes/apps/radiosity/",                        "./RADIOSITY -p 4 -ae 5000 -bf 0.1 -en 0.05 -room -batch"],
    ["raytrace",        "codes/apps/raytrace/",                         "./RAYTRACE -p4 -m64 inputs/car.env"],
    ["volrend",         "codes/apps/volrend/",                          "./VOLREND 4 inputs/head 8"],
    ["water-nsquared",  "codes/apps/water-nsquared/",                   "./WATER-NSQUARED < inputs/n512-p4"],
    ["water-spatial",   "codes/apps/water-spatial//",                   "./WATER-SPATIAL < inputs/n512-p4"],
    ["cholesky",        "codes/kernels/cholesky/",                      "./CHOLESKY -p4 < inputs/tk15.O"],
    ["fft",             "codes/kernels/fft/",                           "./FFT -p4 -m16"],
    ["lu",              "codes/kernels/lu/non_contiguous_blocks",       "./LU -p4 -n512"],
    ["radix",           "codes/kernels/radix/",                         "./RADIX -p4 -n1048576"]
    ]   

for bench_name, bench_dir, bench_command in list_benchmark:
    dir_to_go = benchmark_base_dir + bench_dir
    os.chdir (dir_to_go)
    print (os.getcwd())

    script_to_run = pin_base_dir + pin_program
    script_to_run += " -t " + pin_base_dir + pin_tool
    script_to_run += " -o " + output_dir + bench_name + ".log"
    script_to_run += " -limit " + access_limit
    script_to_run += " -- "
    script_to_run += bench_command

    print (script_to_run)
    os.system (script_to_run)

MyPinTool.cpp

PIN directory의 source/tools/MyPinTool/MyPinTool.cpp 파일이다.

/*! @file
 *  This is an example of the PIN tool that demonstrates some basic PIN APIs 
 *  and could serve as the starting point for developing your first PIN tool
 */

#include "pin.H"
#include <iostream>
#include <fstream>
#include <time.h>

/* ================================================================== */
// Global variables 
/* ================================================================== */

UINT64 insCount = 0;        //number of dynamically executed instructions
UINT64 bblCount = 0;        //number of dynamically executed basic blocks
UINT64 threadCount = 0;     //total number of threads, including main thread

PIN_LOCK lock;
FILE* pFileOut;

/* ===================================================================== */
// Command line switches
/* ===================================================================== */
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE,  "pintool",
    "o", "", "specify file name for MyPinTool output");

KNOB<UINT64> KnobLimit(KNOB_MODE_WRITEONCE,  "pintool",
    "limit", "10000000", "limit the number of lines of the output file");

KNOB<BOOL>   KnobCount(KNOB_MODE_WRITEONCE,  "pintool",
    "count", "1", "count instructions, basic blocks and threads in the application");



/* ===================================================================== */
// Utilities
/* ===================================================================== */

/*!
 *  Print out help message.
 */
INT32 Usage()
{
    cerr << KNOB_BASE::StringKnobSummary() << endl;

    return -1;
}

/* ===================================================================== */
// Analysis routines
/* ===================================================================== */

/*!
 * Increase counter of the executed basic blocks and instructions.
 * This function is called for every basic block when it is about to be executed.
 * @param[in]   numInstInBbl    number of instructions in the basic block
 * @note use atomic operations for multi-threaded applications
 */
VOID CountBbl(UINT32 numInstInBbl)
{
    PIN_GetLock (&lock, 1);
    bblCount++;
    insCount += numInstInBbl;
    PIN_ReleaseLock (&lock);
}

/* ===================================================================== */
// Instrumentation callbacks
/* ===================================================================== */

/*!
 * Insert call to the CountBbl() analysis routine before every basic block 
 * of the trace.
 * This function is called every time a new trace is encountered.
 * @param[in]   trace    trace to be instrumented
 * @param[in]   v        value specified by the tool in the TRACE_AddInstrumentFunction
 *                       function call
 */
VOID Trace(TRACE trace, VOID *v)
{
    // Visit every basic block in the trace
    for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl))
    {
        // Insert a call to CountBbl() before every basic bloc, passing the number of instructions
        BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)CountBbl, IARG_UINT32, BBL_NumIns(bbl), IARG_END);
    }
}

/*!
 * Increase counter of threads in the application.
 * This function is called for every thread created by the application when it is
 * about to start running (including the root thread).
 * @param[in]   threadIndex     ID assigned by PIN to the new thread
 * @param[in]   ctxt            initial register state for the new thread
 * @param[in]   flags           thread creation flags (OS specific)
 * @param[in]   v               value specified by the tool in the 
 *                              PIN_AddThreadStartFunction function call
 */
VOID ThreadStart(THREADID threadIndex, CONTEXT *ctxt, INT32 flags, VOID *v)
{
    PIN_GetLock (&lock, 1);
    threadCount++;
    PIN_ReleaseLock (&lock);
}

UINT64 accessCount = 0;    // number of memory access
UINT64 accessCountLimit = 10 * 1000 * 1000;

#define ACCESS_READ    1
#define ACCESS_WRITE    2

VOID RecordMemAccess (int rw, VOID* addr)
{
    PIN_GetLock (&lock, 1);

    accessCount ++;
    if (accessCount > accessCountLimit)
    {
        fprintf (stderr, "==== Access count reached %llu\n", accessCountLimit);
        fflush (stderr);
        PIN_ReleaseLock (&lock);
        PIN_ExitApplication(0);
        return;
    }

    if (rw == ACCESS_READ)
    {
        fprintf (pFileOut, "R %p\n", addr);
    }
    else
    {
        fprintf (pFileOut, "W %p\n", addr);
    }

    fflush (pFileOut);
    PIN_ReleaseLock (&lock);
}

VOID RecordMemRead(VOID* ip, VOID* addr)
{
    RecordMemAccess(ACCESS_READ, addr);
}

VOID RecordMemWrite(VOID* ip, VOID* addr)
{
    RecordMemAccess(ACCESS_WRITE, addr);
}

VOID Instruction(INS ins, VOID* v)
{
    // Instruments memory accesses using a predicated call, i.e.
    // the instrumentation is called iff the instruction will actually be executed.
    //
    // On the IA-32 and Intel(R) 64 architectures conditional moves and REP 
    // prefixed instructions appear as predicated instructions in Pin.
    UINT32 memOperands = INS_MemoryOperandCount(ins);
    
    // Iterate over each memory operand of the instruction.
    for (UINT32 memOp = 0; memOp < memOperands; memOp++)
    {   
        if (INS_MemoryOperandIsRead(ins, memOp))
        {   
            INS_InsertPredicatedCall(
            ins, IPOINT_BEFORE, (AFUNPTR)RecordMemRead,
            IARG_INST_PTR,
            IARG_MEMORYOP_EA, memOp,
            IARG_END);
        }
        // Note that in some architectures a single memory operand can be 
        // both read and written (for instance incl (%eax) on IA-32)
        // In that case we instrument it once for read and once for write.
        if (INS_MemoryOperandIsWritten(ins, memOp))
        {   
            INS_InsertPredicatedCall(
            ins, IPOINT_BEFORE, (AFUNPTR)RecordMemWrite,
            IARG_INST_PTR,
            IARG_MEMORYOP_EA, memOp,
            IARG_END);
        }
    }
}

/*!
 * Print out analysis results.
 * This function is called when the application exits.
 * @param[in]   code            exit code of the application
 * @param[in]   v               value specified by the tool in the 
 *                              PIN_AddFiniFunction function call
 */
VOID Fini(INT32 code, VOID *v)
{
    PIN_GetLock (&lock, 1);
    
    cerr <<  "===============================================" << endl;
    cerr <<  "MyPinTool analysis results: " << endl;
    cerr <<  "Number of instructions: " << insCount  << endl;
    cerr <<  "Number of basic blocks: " << bblCount  << endl;
    cerr <<  "Number of threads: " << threadCount  << endl;
    cerr <<  "Number of memory accesses: " << accessCount << endl;
    time_t timeNow;
    timeNow = time (NULL);
    cerr <<  " Current time is " << ctime (&timeNow);
    cerr <<  "===============================================" << endl;
    fclose(pFileOut);
    PIN_ReleaseLock (&lock);
}

/*!
 * The main procedure of the tool.
 * This function is called when the application image is loaded but not yet started.
 * @param[in]   argc            total number of elements in the argv array
 * @param[in]   argv            array of command line arguments, 
 *                              including pin -t <toolname> -- ...
 */
int main(int argc, char *argv[])
{
    PIN_InitLock (&lock);

    // Initialize PIN library. Print help message if -h(elp) is specified
    // in the command line or the command line is invalid 
    if( PIN_Init(argc,argv) )
    {
        return Usage();
    }
    
    string fileName = KnobOutputFile.Value();

    if (fileName.empty())
    {
        return Usage();
    }

    pFileOut = fopen (fileName.c_str(), "w");
    if (pFileOut == NULL)
    {
        fprintf (stderr, "Cannot open output file\n");
        return (-1);
    }

    accessCountLimit = KnobLimit.Value();

    if (KnobCount)
    {
        // Register function to be called to instrument traces
        TRACE_AddInstrumentFunction(Trace, 0);

        INS_AddInstrumentFunction(Instruction, 0);

        // Register function to be called for every thread before it starts running
        PIN_AddThreadStartFunction(ThreadStart, 0);

        // Register function to be called when the application exits
        PIN_AddFiniFunction(Fini, 0);
    }
    
    fprintf (stderr,"===============================================\n");
    fprintf (stderr,"Output goes to %s\n", fileName.c_str());

    time_t timeNow;
    timeNow = time (NULL);
    fprintf (stderr, "Current time is %s", ctime (&timeNow));

    // Start the program, never returns
    PIN_StartProgram();
    
    return 0;
}

/* ===================================================================== */
/* eof */
/* ===================================================================== */

2018년 6월 29일

Chang Park