bare impressions on avx made
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
cmake_minimum_required(VERSION 4.4.3)
|
||||||
|
project(simd_avx_example)
|
||||||
|
|
||||||
|
add_compile_options(-Wall -Werror -std=c++23 -march=native -O3)
|
||||||
|
|
||||||
|
FILE(GLOB sourcefiles src/*.c*)
|
||||||
|
add_executable(simd_avx_example ${sourcefiles})
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Performance Test
|
||||||
|
|
||||||
|
Example Project to test the performance increase of avx multiplication in contrast to standard multiplication by multiplying 16 32bit integer a fixed number of times.
|
||||||
|
|
||||||
|
## Programstructure
|
||||||
|
|
||||||
|
The program creates two arrays with each containing 16 integers which are dispatched to a thread in a timer environment to track the execution time. The results are then printed to stdout.
|
||||||
|
|
||||||
|
## Benchmark by 2147483647 iterations
|
||||||
|
|
||||||
|
| | ./program | nice -20 program | chrt -f 99 program |
|
||||||
|
| :---: | :---: | :---: | :---: |
|
||||||
|
| AVX | 9.46229s | 6.651s | 11.7455s |
|
||||||
|
| Std | 16.7001s | 13.6682s | 16.828s |
|
||||||
+125
@@ -0,0 +1,125 @@
|
|||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <immintrin.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#define _ITERATIONS std::numeric_limits<int>::max()
|
||||||
|
|
||||||
|
typedef __m512i vec16a32;
|
||||||
|
|
||||||
|
std::mutex cout_mutex;
|
||||||
|
|
||||||
|
void avx_mult32(uint32_t* a1, uint32_t* a2, __m512i* out){
|
||||||
|
vec16a32 v1 = _mm512_loadu_si512(a1);
|
||||||
|
vec16a32 v2 = _mm512_loadu_si512(a2);
|
||||||
|
*out = _mm512_mullo_epi32(v1, v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void avx_mult_example(uint32_t* values1, uint32_t* values2, vec16a32* out){
|
||||||
|
asm volatile(
|
||||||
|
""
|
||||||
|
:
|
||||||
|
: "m"(values1), "m"(values2)
|
||||||
|
: "memory"
|
||||||
|
);
|
||||||
|
|
||||||
|
avx_mult32(values1, values2, out);
|
||||||
|
|
||||||
|
asm volatile(
|
||||||
|
""
|
||||||
|
:
|
||||||
|
: "v"(out)
|
||||||
|
: "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void standard_mult_example(uint32_t* values1, uint32_t* values2, uint32_t* out){
|
||||||
|
asm volatile(
|
||||||
|
""
|
||||||
|
:
|
||||||
|
: "m"(values1), "m"(values2)
|
||||||
|
: "memory"
|
||||||
|
);
|
||||||
|
|
||||||
|
for(int i = 0; i < 16; i++) out[i] = values1[i] * values2[i];
|
||||||
|
|
||||||
|
asm volatile(
|
||||||
|
""
|
||||||
|
:
|
||||||
|
: "m"(out)
|
||||||
|
: "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Timer{
|
||||||
|
private:
|
||||||
|
std::chrono::time_point<std::chrono::high_resolution_clock> start;
|
||||||
|
std::chrono::time_point<std::chrono::high_resolution_clock> end;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Timer(std::string name, auto&& func) : name(name){
|
||||||
|
start = std::chrono::high_resolution_clock::now();
|
||||||
|
func();
|
||||||
|
end = std::chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer(std::string name, auto&& func, int iterations) : name(name){
|
||||||
|
start = std::chrono::high_resolution_clock::now();
|
||||||
|
for(int i = 0; i < iterations; i++) func();
|
||||||
|
end = std::chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
long long get_duration() const {
|
||||||
|
return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_duration() const {
|
||||||
|
std::scoped_lock lock(cout_mutex);
|
||||||
|
auto duration = get_duration();
|
||||||
|
std::cout << name << " took " << duration << "μs, ";
|
||||||
|
std::cout << duration / 1000.0 << "ms, ";
|
||||||
|
std::cout << duration / 1000000.0 << "s." << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::thread dispatch_timer(std::string name, auto&& func, int iterations){
|
||||||
|
return std::thread(
|
||||||
|
[name = std::move(name), func = std::forward<decltype(func)>(func), iterations]() mutable {
|
||||||
|
Timer timer(name, func, iterations);
|
||||||
|
timer.print_duration();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_pseudorandom(uint32_t* arr, size_t size){
|
||||||
|
for(size_t i = 0; i < size; i++) arr[i] = std::rand() % 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
std::srand(42);
|
||||||
|
auto array_size = 16;
|
||||||
|
|
||||||
|
uint32_t values1[array_size];
|
||||||
|
uint32_t values2[array_size];
|
||||||
|
|
||||||
|
fill_pseudorandom(values1, array_size);
|
||||||
|
fill_pseudorandom(values2, array_size);
|
||||||
|
|
||||||
|
std::cout << "For " << _ITERATIONS << " iterations:" << std::endl;
|
||||||
|
|
||||||
|
vec16a32 avx_result;
|
||||||
|
uint32_t standard_result[16];
|
||||||
|
|
||||||
|
std::thread standard_thread = dispatch_timer("Standard multiplication", [&](){ standard_mult_example(values1, values2, standard_result); }, _ITERATIONS);
|
||||||
|
std::thread avx_thread = dispatch_timer("AVX multiplication", [&](){ avx_mult_example(values1, values2, &avx_result); }, _ITERATIONS);
|
||||||
|
|
||||||
|
avx_thread.join();
|
||||||
|
standard_thread.join();
|
||||||
|
|
||||||
|
(void)avx_result;
|
||||||
|
(void)standard_result;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user