├── .gitignore ├── CMakeLists.txt ├── README.md └── particle-repel.cc /.gitignore: -------------------------------------------------------------------------------- 1 | wasi-sdk-5.0* 2 | build 3 | /CMakeFiles 4 | /cmake_install.cmake 5 | /build-wasm 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | 3 | project(particle-repl) 4 | 5 | if (NOT CMAKE_BUILD_TYPE) 6 | message(STATUS "No build type selected, default to Release") 7 | set(CMAKE_BUILD_TYPE "Release") 8 | endif() 9 | 10 | message(STATUS "Building in ${CMAKE_BUILD_TYPE} mode") 11 | message(STATUS "Building with ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION} on ${CMAKE_SYSTEM}") 12 | 13 | set(CMAKE_CXX_FLAGS "$ENV{CFLAGS} -std=c++11 -O2") 14 | 15 | add_executable(particle-repel-simd 16 | particle-repel.cc 17 | ) 18 | set_target_properties(particle-repel-simd PROPERTIES COMPILE_FLAGS "-msimd128") 19 | 20 | add_executable(particle-repel 21 | particle-repel.cc 22 | ) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ Wasm SIMD 128 example 2 | 3 | This repo is made to showcase how to emit Wasm SIMD 128 instructions from C++, and use it with [Wasmer](https://github.com/wasmerio/wasmer). 4 | 5 | ## Build 6 | 7 | ```bash 8 | ./build.sh 9 | ``` 10 | 11 | It will download the [WASI SDK 5](https://github.com/CraneStation/wasi-sdk/releases/tag/wasi-sdk-5) (the macOS or linux version, depending on your system). 12 | 13 | ## Run it! 14 | 15 | You can run the SIMD version with [Wasmer](https://wasmer.io/): 16 | 17 | ```bash 18 | wasmer run --backend=llvm --enable-simd build/particle-repel-simd.wasm 19 | ``` 20 | 21 | Or run the non-SIMD version 22 | 23 | ```bash 24 | wasmer run --backend=llvm build/particle-repel.wasm 25 | ``` 26 | 27 | Or run the Native SIMD version 28 | 29 | ```bash 30 | ./build/particle-repel 31 | ``` 32 | 33 | 34 | After running it, the results should be something like: 35 | 36 | ``` 37 | Beginning simulation. 38 | 5.44089e-21 39 | 9.19434e-22 40 | 9.98077e-20 41 | 1.01414e-17 42 | 1.89481e-17 43 | 3.61239e-16 44 | 2.24645e-14 45 | 1.24237e-13 46 | 2.39859e-13 47 | 3.13435e-13 48 | 4.13311e-13 49 | 6.31935e-13 50 | 4.59395e-10 51 | 6.40114e-10 52 | 1.15545e-09 53 | 2.24414e-10 54 | 1.03695e-09 55 | 4.06071e-10 56 | 7.64414e-10 57 | 7.57652e-10 58 | ``` 59 | 60 | ## Benchmarks 61 | 62 | ``` 63 | # Native 64 | time ./build/particle-repel # 4.930 total 65 | 66 | # Wasmer SIMD 67 | time wasmer run --backend=llvm --enable-simd build/particle-repel-simd.wasm # 4.980 total 68 | 69 | # Wasmer Non-SIMD 70 | time wasmer run --backend=llvm build/particle-repel.wasm # 11.630 total 71 | ``` 72 | -------------------------------------------------------------------------------- /particle-repel.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef float float4 __attribute__((ext_vector_type(4))); 5 | 6 | namespace { 7 | constexpr int object_count = 10000; 8 | 9 | float4 positions[object_count]; 10 | float4 velocities[object_count]; 11 | 12 | void init_positions() { 13 | std::mt19937_64 mt; 14 | std::uniform_real_distribution dist(-127.0, 127.0); 15 | for (int i = 0; i < object_count; ++i) { 16 | positions[i] = (float4){dist(mt), dist(mt), dist(mt), dist(mt)}; 17 | } 18 | } 19 | 20 | void init_velocities() { 21 | for (int i = 0; i < object_count; ++i) { 22 | velocities[i] = (float4){0, 0, 0, 0}; 23 | } 24 | } 25 | 26 | float horizontal_add(float4 x) { 27 | auto partial_sum = x.xy + x.zw; 28 | return partial_sum.x + partial_sum.y; 29 | } 30 | 31 | float distance(float4 x, float4 y) { 32 | float4 d = y - x; 33 | float4 d2 = d * d; 34 | return horizontal_add(d2); 35 | } 36 | 37 | void update() { 38 | for (int i = 0; i < object_count; ++i) { 39 | for (int j = 0; j < object_count; ++j) { 40 | float dist = distance(positions[i], positions[j]); 41 | dist *= dist; 42 | if (dist == 0) 43 | continue; 44 | velocities[i] += (positions[j] - positions[i]) / dist; 45 | } 46 | } 47 | 48 | for (int i = 0; i < object_count; ++i) { 49 | positions[i] += velocities[i]; 50 | } 51 | } 52 | 53 | float avg_motion() { 54 | float4 avg_motion = velocities[0] / (double)object_count; 55 | for (int i = 1; i < object_count; ++i) { 56 | avg_motion += velocities[i] / (double)object_count; 57 | } 58 | return distance((float4){0, 0, 0, 0}, avg_motion); 59 | } 60 | } 61 | 62 | int main(void) { 63 | init_positions(); 64 | init_velocities(); 65 | std::printf("Beginning simulation.\n"); 66 | fflush(stdout); 67 | for (int i = 0; i < 20; ++i) { 68 | update(); 69 | std::printf("%g\n", avg_motion()); 70 | fflush(stdout); 71 | } 72 | } 73 | --------------------------------------------------------------------------------