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