이 튜토리얼에서는 다음을 수행합니다. 소스코드는 여기
이 튜토리얼의 마지막 부분에 간략한 요약이 포함되어 있습니다. 이 튜토리얼을 보완하려면 주저하지 말고 Poplar 및 PopLibs 사용자 가이드를 읽어보세요 . tut5_matrix_vector/start_here작업 디렉터리로 사용하세요 .
IPU에서 이 튜토리얼을 실행하려면 Poplar SDK 환경을 활성화해야 합니다(IPU 시스템 시작 안내서 참조).
또한 C++11 표준과 호환되는 C++ 도구 체인이 필요합니다. 이 튜토리얼의 빌드 명령은 GCC를 사용합니다.
파일에는 matrix-mul-codelets.cpp내적을 수행하는 정점 코드에 대한 개요가 포함되어 있습니다. 입력 및 출력 필드는 이미 정의되어 있습니다.
class DotProductVertex : public Vertex {
public:
Input<Vector<float>> a;
Input<Vector<float>> b;
Output<float> out;
}
정점이 포플러에 계산을 제공하려면 compute방법이 DotProductVertex완료되어야 합니다. 이 메서드는 두 입력 벡터 의 내적을 계산 a하고 b스칼라 결과를 에 저장 해야 합니다 out.
대수적으로 두 벡터 및 의 dot product은 다음과 같이 계산됩니다.
튜토리얼 3의 코드렛을 다시 살펴보는 것도 유용할 수 있습니다 . 팁: compute코드렛의 메소드 내에서 를 사용하여 벡터의 요소 수를 찾을 수 a있습니다 a.size().
호스트 코드는 이전 자습서의 호스트 코드와 유사한 패턴을 따릅니다.
입력 행렬, 입력 벡터 및 출력 벡터에 대해 정의된 세 가지 텐서가 있습니다.
Tensor matrix = graph.addVariable(FLOAT, {numRows, numCols}, "matrix");
Tensor inputVector = graph.addVariable(FLOAT, {numCols}, "inputVector");
Tensor outputVector = graph.addVariable(FLOAT, {numRows}, "outputVector");
이 함수는 buildMultiplyProgram곱셈을 수행하기 위한 그래프와 제어 프로그램을 생성합니다. 제어 프로그램은 이라는 단일 계산 세트를 실행합니다 mulCS. 이 컴퓨팅 세트는 출력 벡터의 각 출력 요소에 대한 꼭짓점(즉, 입력 행렬의 각 행에 대한 하나의 꼭짓점)으로 구성됩니다.
이 자습서의 다음 작업은 호스트 코드를 작성하여 컴퓨팅 세트에 정점을 추가하는 것입니다.
반복을 수행하는 루프를 생성합니다 numRows. 각 반복은 그래프에 정점을 추가합니다. t힌트: 모양이 인 포플러 텐서가 주어지면 를 사용하여 i번째 차원의 크기를 얻을 수 있습니다 . 예를 들면 .{numRows, numCols}t.dim(i)numRows == t.dim(0)
그래프 개체의 기능을 사용하여 컴퓨팅 세트 에 addVertex정점 유형을 추가합니다 . 튜토리얼 3 에서 정점을 추가한 방법을 다시 살펴보는 것이 도움이 될 수 있습니다 .DotProductVertexmulCS
addVertex의 마지막 인수를 사용하여 정점의 필드를 해당 행의 관련 텐서 슬라이스에 연결합니다. v예를 들어, 입력 in과 출력이 있는 꼭지점을 만들고 싶고 out그래프에서 이미 동일한 이름을 가진 두 개의 텐서를 정의했다고 가정하면 다음과 같이 꼭지점을 만들 수 있습니다.
VertexRef v = graph.addVertex(computeSet, "v", {{"in", in}, {"out", out}});
이 경우 각 꼭지점은 행렬의 한 행(텐서에서 인덱스 연산자를 사용할 수 있습니다 matrix. 예를 들어 i번째 행은 matrix[i])과 전체 in텐서를 가져와서 텐서의 단일 요소로 출력합니다 out.
새로 생성된 정점을 타일에 매핑합니다. i가 우리가 속한 루프의 카운터라면 정점을 타일에 매핑할 수 있습니다 i. 다시 한 번 튜토리얼 3에서 이 작업을 어떻게 수행했는지 확인하는 것이 도움이 될 수 있습니다.
마지막으로 graph.setPerfEstimate()이 정점을 실행하는 데 걸리는 예상 주기 수를 지정하는 데 사용합니다. 이는 장치를 사용할 때만 필요하며 IPUModel프로파일링 외에는 실제로 중요하지 않습니다. 따라서 임의의 정수를 설정할 수 있습니다.
이 코드를 추가한 후 예제를 빌드하고 실행할 수 있습니다. 프로그램을 컴파일하기 위해 makefile이 제공됩니다. 실행하여 빌드할 수 있습니다.make
호스트 프로그램 코드에서 볼 수 있듯이 행렬의 크기를 지정하는 실행 명령에 두 개의 인수를 제공해야 합니다. 예를 들어, 아래와 같이 프로그램을 실행하면 40x50 행렬에 크기 50의 벡터가 곱해집니다.
./tut5_start_here 40 50
호스트 코드에는 결과의 정확성에 대한 검사가 포함됩니다.
이 섹션에서는 IPU 하드웨어를 사용하도록 프로그램을 수정하는 방법을 설명합니다.
#include <poplar/DeviceManager.hpp>
#include <algorithm>
IPUModel ipuModel;
Device device = ipuModel.createDevice();
// Create the DeviceManager which is used to discover devices
auto manager = DeviceManager::createDeviceManager();
// Attempt to attach to a single IPU:
auto devices = manager.getDevices(poplar::TargetType::IPU, 1);
std::cout << "Trying to attach to IPU\n";
auto it = std::find_if(devices.begin(), devices.end(), [](Device &device) {
return device.attach();
});
if (it == devices.end()) {
std::cerr << "Error attaching to device\n";
return 1; //EXIT_FAILURE
}
auto device = std::move(*it);
std::cout << "Attached to IPU " << device.getId() << std::endl;
이는 호스트에 연결된 단일 IPU로 구성된 모든 장치 목록을 가져오고 성공할 때까지 각 장치에 차례로 연결을 시도합니다. 이는 호스트에 여러 사용자가 있는 경우 유용한 접근 방식입니다. 해당 기능과 함께 장치 관리자 ID를 사용하여 특정 장치를 가져오는 것도 가능합니다 getDevice.
graph.setPerfEstimate(v, 20);
DotProductVertex이 줄은 주어진 꼭짓점에 대해 계산이 수행되는 주기 수에 대한 추정치를 제공하며, 이 튜토리얼에서 와 같이 IPU 모델을 사용하고 사용자 정의 꼭짓점을 작성할 때만 필요합니다. IPU 하드웨어를 사용할 때 튜토리얼 4와 같이 프로그램을 프로파일링하기로 결정하면 사이클이 측정됩니다 .
g++ --std=c++11 tut5_ipu_hardware.cpp -lpoplar -lpoputil -o tut5_ipu
이를 실행하기 전에 IPU에 연결하기 위해 시스템이 올바르게 구성되었는지 확인해야 합니다(IPU 시스템 시작 안내서 참조 ).
./tut5_ipu_hardware
이 튜토리얼에서는 사용자 정의 정점을 사용하여 행렬-벡터 곱셈을 수행하는 프로그램을 작성했습니다. 코드렛 자체는 두 벡터 사이의 내적을 계산합니다. 행렬과 벡터 사이의 곱셈을 계산하기 위해 우리는 이러한 정점 중 여러 개를 포플러 그래프에 추가했습니다(행렬의 각 행에 대해 하나씩). 마지막으로 이를 적절한 행과 텐서에 연결했습니다. 이러한 정점은 모두 동일한 컴퓨팅 세트에 추가되었습니다. 즉, IPU에서 병렬로 실행됩니다. 우리는 IPU 모델에서 프로그램을 실행하지만 IPU 하드웨어에서 실행하려면 어떤 변경이 필요한지 살펴보았습니다.