@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそ、すべては、SystemC v0.9公開から始まった
チュートリアルは、次の7つ。
* Tutorial 1: programs and variables
* Tutorial 2: using Poplibs
* Tutorial 3: writing vertex code
* Tutorial 4: profiling output
* Tutorial 5: a basic machine learning example
* Tutorial 6: matrix-vector multiplication
* Tutorial 7: matrix-vector multiplication optimisation
この例題を見ることで、IPU がどんな感じに動くか、妄想できるかな?
Tutorial1の tutorials/poplar/tut1_variables/complete/tut1_ipu_model_complete.cpp を見ていきます。
int main() {
// Create the IPU model device
IPUModel ipuModel;
Device device = ipuModel.createDevice();
Target target = device.getTarget();
ここまでは、IPUのモデル(実機ではない)、のDevice と Target を獲得しています。
// Create the Graph object
Graph graph(target);// Add variables to the graph
Tensor v1 = graph.addVariable(FLOAT, {4}, "v1");
Tensor v2 = graph.addVariable(FLOAT, {4}, "v2");
Tensor v3 = graph.addVariable(FLOAT, {4, 4}, "v3");
Tensor v4 = graph.addVariable(INT, {10}, "v4");
ここまでで、Graph を作って、Variable (v1/v2/v3/v4) を追加します。
// Allocate v1 to reside on tile 0
graph.setTileMapping(v1, 0);// Spread v2 over tiles 0..3
for (unsigned i = 0; i < 4; ++i)
graph.setTileMapping(v2[i], i);// Allocate v3, t4 to tile 0
graph.setTileMapping(v3, 0);
graph.setTileMapping(v4, 0);
ここまでで、各 Variable をどこの Tile (0) にマッピングするかを指定します。
// Add a constant tensor to the graph
Tensor c1 = graph.addConstant<float>(FLOAT, {4}, {1.0, 1.5, 2.0, 2.5});
graph.setTileMapping(c1, 0);
定数を Tile (0) にマッピングします。
// Create a control program that is a sequence of steps
program::Sequence prog;// Add a step to initialize v1 with the constant value in c1
prog.add(program::Copy(c1, v1));
// Debug print the tensor to the host console
prog.add(program::PrintTensor("v1-debug", v1));// Copy the data in v1 to v2
prog.add(program::Copy(v1, v2));
// Debug print v2
prog.add(program::PrintTensor("v2-debug", v2));
シーケンシャルな Program を定義し、以下の4つのプログラムを追加します。
- Copy(c1, v1)
- PrintTensor("v1-debug", v1)
- Copy(v1, v2)
- PrintTensor("v2-debug", v2)
この4つのプログラムはシーケンシャルに実行します。
つまり、c1 => v1 => v2 の代入になります。
// Create host read/write handles for v3
graph.createHostWrite("v3-write", v3);
graph.createHostRead("v3-read", v3);
ここで、v3 を ホストから Write/Read できるようにします。
// Copy a slice of v1 into v3
Tensor v1slice = v1.slice(0, 3);
Tensor v3slice = v3.slice({1, 1}, {2, 4});
prog.add(program::Copy(v1slice, v3slice));
v1のスライス (0,1) を v3のスライス({1,1}, {2,4}) にコピーします。
// Add a data stream to fill v4
DataStream inStream = graph.addHostToDeviceFIFO("v4-input-stream", INT, 10);
ホストからデバイスへのINT型のデータを10を入力します。
// Add program steps to copy from the stream
prog.add(program::Copy(inStream, v4));
prog.add(program::PrintTensor("v4-0", v4));
prog.add(program::Copy(inStream, v4));
prog.add(program::PrintTensor("v4-1", v4));
以下の4つのプログラムを追加します。
- Copy(inStream, v4)
- PrintTensor("v4-0", v4)
- Copy(inStream, v4)
- PrintTensor("v4-1", v4)
この4つのプログラムはシーケンシャルに実行します。
GraphとProgramuをEngineに割当、Device にロードします。
// Create the engine
Engine engine(graph, prog);
engine.load(device);
Variable v3 にデータ(3*3の0)を書き込みます。
// Copy host data via the write handle to v3 on the device
std::vector<float> h3(4 * 4, 0);
engine.writeTensor("v3-write", h3.data());
inDataを初期化し、v4-input-stream に接続します。
// Create a buffer to hold data to be fed via the data stream
std::vector<int> inData(10 * 3);
for (unsigned i = 0; i < 10 * 3; ++i)
inData[i] = i;// Connect the data stream
engine.connectStream("v4-input-stream", &inData[0], &inData[10 * 3]);
プログラムを実行 (run) します。
// Run the control program
std::cout << "Running program\n";
engine.run(0);
std::cout << "Program complete\n";
実行結果の v3 を h3 にコピーし、結果を表示します。
// Copy v3 back to the host via the read handle
engine.readTensor("v3-read", h3.data());// Output the copied back values of v3
std::cout << "\nh3 data:\n";
for (unsigned i = 0; i < 4; ++i) {
std::cout << " ";
for (unsigned j = 0; j < 4; ++j) {
std::cout << h3[i * 4 + j] << " ";
}
std::cout << "\n";
}return 0;
}
tutorials/poplar/tut1_variables/complete/tut1_ipu_hardware_complete.cpp では、
// Create the IPU model device
IPUModel ipuModel;
Device device = ipuModel.createDevice();
を DeviceManager を使って、実機のDeviceを見つけているだけで、それ以外は同じです。
// Create the DeviceManager which is used to discover devices
DeviceManager manager = DeviceManager::createDeviceManager();// Attempt to attach to a single IPU:
Device device;
bool success = false;
// Loop over all single IPU devices on the host
// Break the loop when an IPU is successfully acquired
for (auto &hwDevice : manager.getDevices(poplar::TargetType::IPU, 1)) {
device = std::move(hwDevice);
std::cerr << "Trying to attach to IPU " << device.getId() << std::endl;
if ((success = device.attach())) {
std::cerr << "Attached to IPU " << device.getId() << std::endl;
break;
}
}
if (!success) {
std::cerr << "Error attaching to device" << std::endl;
return -1;
}