IB SessionDownload현재는 라이브러리를 외부에 공개하지 않으며 InfoLab의 일원에게만 배포하고 있습니다. Strength
Basic Concept세션이라는 개념을 씁니다. 세션은 오브젝트로 생성되며, 생성되는 즉시 원격지와의 연결을 시도합니다. 생성된 세션끼리는 서로 독립적이며 간섭하지 않습니다. 기본적으로 다음과 같은 순서가 지켜져야 합니다.
How to import모든 실행 환경은 CentOS와 gcc기준입니다. 라이브러리 없이 코드로만 이용하는 경우 ib_session.h와 ib_session.cpp파일을 받으시고, 다음과 같이 파일을 include 해주세요.
#include "ib_session.h"
gcc로 컴파일 하실때, -libverbs와 --std=c++11 옵션을 넣어주시면 됩니다. 동적 라이브러리를 이용하는 경우 현재 지원되지 않습니다. (구현중입니다) FunctionsConstructor
ib_session::ib_session(const size_t ib_device_num, const int ib_physical_port_num, const char* dest_ip_addr, const uint16_t ip_port, const bool is_responder) 인피니밴드 세션의 생성자입니다. 생성자에서 인피니밴드 카드의 초기화 및 상대방 호스트와의 연결과정까지 모두 처리합니다. 세션은 1:1로만 연결됩니다 (1:N, N:N 불가)
Register Memory
int ib_session::mem_reg(void* host_mem_addr, const size_t host_mem_size) 인피니밴드가 이용할 수 있도록 메인 메모리에 미리 할당된 주소와 크기를 등록합니다. 등록된 메모리는 보조기억장치로 swap되지 않습니다.
성공하면 인피니밴드에 등록한 메모리에 대한 handle이 반환됩니다. handle의 값은 int로, 가장 먼저 등록한 메모리가 0, 그 다음이 1과 같은 식으로 0부터 시작하여 순차적으로 올라가게끔 부여됩니다. ib_session::sending(), ib_session::receiving(), ib_session::mem_dereg()와 같은 함수들은 모두 handle을 이용해 처리합니다. 실패하면 -1을 반환합니다. Deregister Memory
void ib_session::mem_dereg(const size_t host_mem_handle)
인피니밴드가 더이상 사용할 수 없도록 메모리를 해제합니다.
리턴값은 없습니다. Send Message
int ib_session::sending(const size_t host_mem_handle, const size_t host_mem_offset, const size_t message_size)
등록한 메모리에서, 지정한 오프셋부터 특정 사이즈만큼의 내용을 원격 호스트로 보냅니다.
성공하면 0이, 실패하면 -1이 반환됩니다. 비동기화가 구현되어있지 않습니다. 따라서 함수의 실행이 끝날때까지 다음 라인으로 넘어가지 않습니다. Receive Message
int ib_session::receiving(const size_t host_mem_handle, const size_t host_mem_offset, const size_t message_size)
등록한 메모리에서, 원격 호스트가 보낸 메시지를 지정한 오프셋부터 특정 사이즈만큼 받습니다.
성공하면 0이, 실패하면 -1이 반환됩니다. 비동기화가 구현되어있지 않습니다. 따라서 함수의 실행이 끝날때까지 다음 라인으로 넘어가지 않습니다. Thread-Safe & Synchronize to Remote Host이전에도 언급했듯, 해당 라이브러리는 현재 쓰레드-안전과 원격지와의 연결 순서에 조금 문제가 있습니다. 우선 추천드리는 방법은 다음과 같습니다.
쓰레드가 전환되기 때문에, 인피니밴드에서 오는 MSI-X 인터럽트가 제대로 처리되지 못해 큰 병목이 생깁니다! (쓰레드 전환을 방지하시려면 Thread Affinity 관련 함수를 사용하십시요) (테스트 결과 최대 전송 성능의 절반정도 나옵니다.) Node 1 (Master)
void test_thread(ib_session& session, int& handle) { session.sending(handle, 0, message_size); } int main() { ib_session s_1(0, 1, nullptr, 8282, true); ib_session s_2(0, 1, nullptr, 8282, true); size_t message_size = 12; uint8_t* buffer_1 = (uint8_t*)malloc(message_size); uint8_t* buffer_2 = (uint8_t*)malloc(message_size); int handle_1 = s_1.mem_reg(buffer_1); int handle_2 = s_2.mem_reg(buffer_2); auto t_1 = std::async(std::launch::async, [&]{ test_thread(s_1, handle_1); } ); auto t_2 = std::async(std::launch::async, [&]{ test_thread(s_2, handle_2); } ); t_1.get(); t_2.get(); free(buffer_1); free(buffer_2); return 0; } Node 2 (Slave)
void test_thread(ib_session& session, int& handle) { session.receiving(handle, 0, message_size); } int main() { ib_session s_1(0, 1, "172.30.1.17", 8282, false); ib_session s_2(0, 1, "172.30.1.17", 8282, false); size_t message_size = 12; uint8_t* buffer_1 = (uint8_t*)malloc(message_size); uint8_t* buffer_2 = (uint8_t*)malloc(message_size); int handle_1 = s_1.mem_reg(buffer_1); int handle_2 = s_2.mem_reg(buffer_2); auto t_1 = std::async(std::launch::async, [&]{ test_thread(s_1, handle_1); } ); auto t_2 = std::async(std::launch::async, [&]{ test_thread(s_2, handle_2); } ); t_1.get(); t_2.get(); free(buffer_1); free(buffer_2); return 0; }
현재 반복 전송에서 가장 높은 전송 성능을 보여줍니다. Node 1 (Master)
void test_thread(ib_session& session, int& handle_1, int& handle_2) { session.sending(handle_1, 0, message_size); session.sending(handle_2, 0, message_size); } int main() { ib_session s(0, 1, nullptr, 8282, true); size_t message_size = 12; uint8_t* buffer_1 = (uint8_t*)malloc(message_size); uint8_t* buffer_2 = (uint8_t*)malloc(message_size); int handle_1 = s.mem_reg(buffer_1); int handle_2 = s.mem_reg(buffer_2); auto t = std::async(std::launch::async, [&]{ test_thread(s, handle_1, handle_2); } ); t.get(); free(buffer); return 0; } Node 2 (Slave)
void test_thread(ib_session& session, int& handle_1, int& handle_2) { session.receiving(handle_1, 0, message_size); session.receiving(handle_2, 0, message_size); } int main() { ib_session s(0, 1, "172.30.1.17", 8282, false); size_t message_size = 12; uint8_t* buffer_1 = (uint8_t*)malloc(message_size); uint8_t* buffer_2 = (uint8_t*)malloc(message_size); int handle_1 = s.mem_reg(buffer_1); int handle_2 = s.mem_reg(buffer_2); auto t = std::async(std::launch::async, [&]{ test_thread(s, handle_1, handle_2); } ); t.get(); free(buffer); return 0; } 이 방법 이외에는 추천하지 않습니다. malloc()의 위치는 쓰레드 안이든, 밖이든 상관이 없습니다. 반드시 송수신 되기 전에 할당된 메모리가 등록되어있고, 그것의 핸들을 알고있으면 됩니다. |