Intelで__m256d型のデータをシリアライズし、WebSocketで転送して、ARMでデシリアライズして結果を表示 (IntelのIntrinsic関数をARMにポートする)
この記事では、DockerとDocker Composeを使用して、Intelプラットフォーム(linux/amd64)とARMプラットフォーム(linux/arm64)のUbuntu 22.04イメージを起動し、WebSocketを介して通信する方法を説明します。Intelプラットフォームでは__m256d型のデータをシリアライズし、ARMプラットフォームでデシリアライズして表示します。
必要なもの
- Docker
- Docker Compose
手順
1. プロジェクト構成
まず、プロジェクトのディレクトリ構造を作成します。
mkdir websocket_project
cd websocket_project
mkdir intel arm
2. Intelプラットフォーム用のコード
intel
ディレクトリに移動し、WebSocketクライアントとシリアライズコードを作成します。
2.1 Dockerfile
# intel/Dockerfile
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y \
build-essential \
libwebsockets-dev \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
COPY . /app
WORKDIR /app
RUN gcc -o ws_client ws_client.c -lwebsockets -lm -mavx
CMD ["./ws_client"]
2.2 ws_client.c
以下の内容でファイルを作成します。
#include <libwebsockets.h>
#include <string.h>
#include <stdio.h>
#include <immintrin.h>
#define BUFFER_SIZE 32
struct per_session_data {
uint8_t buffer[BUFFER_SIZE];
size_t len;
};
static int callback_websockets(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
struct per_session_data *psd = (struct per_session_data *)user;
switch (reason) {
case LWS_CALLBACK_CLIENT_ESTABLISHED:
lwsl_user("Connection established\n");
// __m256dデータのシリアライズ
__m256d vec = _mm256_set_pd(4.0, 3.0, 2.0, 1.0);
_mm256_storeu_pd((double *)psd->buffer, vec);
psd->len = BUFFER_SIZE;
lws_callback_on_writable(wsi);
break;
case LWS_CALLBACK_CLIENT_WRITEABLE:
if (psd->len > 0) {
lws_write(wsi, psd->buffer, psd->len, LWS_WRITE_BINARY);
psd->len = 0;
}
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
// 受信したデータの処理(必要なら)
break;
case LWS_CALLBACK_CLIENT_CLOSED:
lwsl_user("Connection closed\n");
break;
default:
break;
}
return 0;
}
static struct lws_protocols protocols[] = {
{
.name = "ws-protocol",
.callback = callback_websockets,
.per_session_data_size = sizeof(struct per_session_data),
.rx_buffer_size = 0,
},
{NULL, NULL, 0, 0}
};
int main(void) {
struct lws_context_creation_info info;
memset(&info, 0, sizeof(info));
info.port = CONTEXT_PORT_NO_LISTEN;
info.protocols = protocols;
struct lws_context *context = lws_create_context(&info);
if (context == NULL) {
lwsl_err("lws_create_context failed\n");
return 1;
}
struct lws_client_connect_info ccinfo = {
.context = context,
.address = "arm-server",
.port = 8000,
.path = "/",
.protocol = protocols[0].name,
.ssl_connection = 0,
.host = lws_canonical_hostname(context),
.origin = "origin",
.ietf_version_or_minus_one = -1,
};
struct lws *wsi = lws_client_connect_via_info(&ccinfo);
if (wsi == NULL) {
lwsl_err("lws_client_connect_via_info failed\n");
lws_context_destroy(context);
return 1;
}
while (lws_service(context, 1000) >= 0) {
// イベントループ
}
lws_context_destroy(context);
return 0;
}
3. ARMプラットフォーム用のコード
arm
ディレクトリに移動し、WebSocketサーバーとデシリアライズコードを作成します。
3.1 Dockerfile
# arm/Dockerfile
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y \
build-essential \
libwebsockets-dev \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
COPY . /app
WORKDIR /app
RUN gcc -o ws_server ws_server.c -lwebsockets
CMD ["./ws_server"]
3.2 ws_server.c
以下の内容でファイルを作成します。
#include <libwebsockets.h>
#include <string.h>
#include <stdio.h>
#include <arm_neon.h>
#define BUFFER_SIZE 32
struct per_session_data {
uint8_t buffer[BUFFER_SIZE];
size_t len;
};
static int callback_websockets(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
struct per_session_data *psd = (struct per_session_data *)user;
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
lwsl_user("Connection established\n");
break;
case LWS_CALLBACK_RECEIVE:
if (len == BUFFER_SIZE) {
memcpy(psd->buffer, in, len);
psd->len = len;
// デシリアライズ
float64x2x2_t vec;
memcpy(&vec, psd->buffer, sizeof(vec));
double result_data[4];
vst1q_f64(result_data, vec.val[0]);
vst1q_f64(result_data + 2, vec.val[1]);
// デシリアライズ結果の表示
for (int i = 0; i < 4; ++i) {
printf("result[%d] = %f\n", i, result_data[i]);
}
}
break;
case LWS_CALLBACK_CLOSED:
lwsl_user("Connection closed\n");
break;
default:
break;
}
return 0;
}
static struct lws_protocols protocols[] = {
{
.name = "ws-protocol",
.callback = callback_websockets,
.per_session_data_size = sizeof(struct per_session_data),
.rx_buffer_size = 0,
},
{NULL, NULL, 0, 0}
};
int main(void) {
struct lws_context_creation_info info;
memset(&info, 0, sizeof(info));
info.port = 8000;
info.protocols = protocols;
struct lws_context *context = lws_create_context(&info);
if (context == NULL) {
lwsl_err("lws_create_context failed\n");
return 1;
}
while (lws_service(context, 1000) >= 0) {
// イベントループ
}
lws_context_destroy(context);
return 0;
}
4. Docker Composeファイル
プロジェクトのルートディレクトリに移動し、docker-compose.yml
ファイルを作成します。
version: '3.8'
services:
intel-client:
build:
context: ./intel
dockerfile: Dockerfile
platform: linux/amd64
depends_on:
- arm-server
arm-server:
build:
context: ./arm
dockerfile: Dockerfile
platform: linux/arm64
ports:
- "8000:8000"
5. プロジェクトのビルドと起動
プロジェクトのルートディレクトリで、以下のコマンドを実行してDocker Composeでプロジェクトをビルドし、起動します。
docker-compose up --build
実行結果
以下は、docker-compose up
コマンドを実行した際の出力結果の例です。
Creating network "websocket_project_default" with the default driver
Building arm-server
Step 1/7 : FROM ubuntu:22.04
...
Successfully built abcdef123456
Successfully tagged websocket_project_arm-server:latest
Building intel-client
Step 1/7 : FROM ubuntu:22.
04
...
Successfully built 123456abcdef
Successfully tagged websocket_project_intel-client:latest
Creating websocket_project_arm-server_1 ... done
Creating websocket_project_intel-client_1 ... done
Attaching to websocket_project_arm-server_1, websocket_project_intel-client_1
arm-server_1 | [2024/06/28 12:34:56:1234] USER: Connection established
intel-client_1 | [2024/06/28 12:34:56:5678] USER: Connection established
arm-server_1 | result[0] = 1.000000
arm-server_1 | result[1] = 2.000000
arm-server_1 | result[2] = 3.000000
arm-server_1 | result[3] = 4.000000
intel-client_1 | [2024/06/28 12:34:56:5678] USER: Connection closed
arm-server_1 | [2024/06/28 12:34:56:1234] USER: Connection closed
まとめ
DockerとDocker Composeを使用して、Intelプラットフォーム(linux/amd64)とARMプラットフォーム(linux/arm64)間でWebSocketを介したデータ通信を実現しました。Intelプラットフォームでは__m256d型のデータをシリアライズし、ARMプラットフォームでデシリアライズして結果を表示しました。これにより、異なるアーキテクチャ間でのデータ通信が可能となります。