通过创建单个线程来运行一段代码可加快执行速度
Running a piece of code by creating a single thread speeds-up execution
我正在使用英特尔 AVX2 和 Posix 线程尝试一些基准测试。 假设我正在尝试查找样本中的最小值。 当我创建一个简单的程序时,我运行avx_min函数。 当我创建一个在内部创建 Posix 线程的程序时,我已经将avx_min的实现更改为如下所示avx_min_thread,但实际实现保持不变。此函数可用于多个线程,并且不需要同步,因为线程不会"冲突"(tid = 0,1,2 等)。
当我编译这两个实现而不指定任何优化标志时,它们会给我相同的时间结果。另一种大小,当我使用 -O3 标志编译它们时,它们会导致不同的执行时间,我无法弄清楚为什么会发生这种情况。
PS:我使用以下方法编译它们:
-
案例 1(不创建线程):g++ -mavx2 -O3 -o avxMinO3 avxMinO3.cpp
-
案例 2(在内部创建一个 posix 线程):g++ -mavx2 avxMinO3_t.cpp -lpthread -O3 -o avxMinO3_t
附言2:
- 第1种情况执行时间:0.34秒
- 第二种情况执行时间:0.049秒
Case 1:
double initialize_input(int32_t** relation, int32_t value_bound, int32_t input_size){
clock_t t;
srand(time(NULL));
t = clock();
for(int32_t i = 0 ; i < input_size ; i++){
(*relation)[i] = rand() % value_bound;
}
t = clock() - t;
return ((double) t) / CLOCKS_PER_SEC;
}
int* avx_min(int32_t** relation, int32_t rel_size, double* function_time){
clock_t tic, tac;
__m256i input_buffer;
int32_t* rel = (*relation);
__m256i min = _mm256_set_epi32(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX);
tic = clock();
for(int i = 0 ; i < rel_size ; i += 8){
input_buffer = _mm256_stream_load_si256((__m256i*)&rel[i]);
min = _mm256_min_epi32(min, input_buffer);
}
tac = clock();
double time_diff = (double)(tac - tic);
(*function_time) = time_diff / CLOCKS_PER_SEC;
int* temp = (int*)&min;
return temp;
}
int main(int argc, char** argv) {
int32_t* relation;
double* function_time;
int32_t input_size = 1024 * 1024 * 1024;
int32_t value_bound = 1000;
int alloc_time = initialize_input(&relation, value_bound, input_size);
int* res = avx_min(&relation, input_size, function_time);
return 0;
}
案例2:
template<typename T>
struct thread_input {
T* relation;
T rel_size;
double function_time;
short numberOfThreads;
short tid;
};
template<typename T, typename S, typename I, typename RELTYPE>
T** createAndInitInputPtr(S numberOfThreads, I rel_size, S value_bound, RELTYPE** relation ){
T **result = new T*[numberOfThreads];
for (int i = 0; i < numberOfThreads; i++) {
result[i] = new T;
result[i]->rel_size = rel_size;
result[i]->relation = (*relation);
result[i]->numberOfThreads = numberOfThreads;
result[i]->tid = i;
}
return result;
}
void* avx_min_t(void* input){
clock_t tic, tac;
struct thread_input<int32_t> *input_ptr;
input_ptr = (struct thread_input<int32_t>*) input;
int32_t* relation = input_ptr->relation;
int32_t rel_size = input_ptr->rel_size;
int32_t start = input_ptr->tid * 8;
int32_t offset = input_ptr->numberOfThreads * 8;
__m256i input_buffer;
__m256i min = _mm256_set_epi32(INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX);
tic = clock();
for(int i = start ; i < rel_size ; i += offset){
input_buffer = _mm256_stream_load_si256((__m256i*)&relation[i]);
min = _mm256_min_epi32(min, input_buffer);
}
tac = clock();
double time_diff = (double)(tac-tic);
time_diff = time_diff / CLOCKS_PER_SEC;
input_ptr->function_time = time_diff;
}
int main(int argc, char* argv[]){
int rel_size = 1024 * 1024 * 1024;
short numberOfThreads = 1;
short value_bound = 1000;
pthread_t* threads = new pthread_t[numberOfThreads];
short flag = 1; // flag to check proper aligned memory allocations
int32_t* relation;
double alloc_time = 0.0;
flag = posix_memalign((void**)&relation, 32, rel_size * sizeof(int32_t));
if(flag) {
std::cout << "Memory allocation problem. Exiting..." << std::endl;
exit(1);
}
alloc_time += initialize_input(&relation, value_bound, rel_size);
struct thread_input<int32_t> **input_ptr = createAndInitInputPtr<struct thread_input<int32_t>, short, int, int32_t>(numberOfThreads, rel_size, value_bound, &relation);
clock_t tic = clock();
for (int i = 0; i < numberOfThreads; i++) {
pthread_create(&threads[i], NULL, avx_min_t,(void*) input_ptr[i]);
}
for (int i = 0; i < numberOfThreads; i++) {
pthread_join(threads[i], NULL);
}
tic = clock()-tic;
double time = tic / CLOCKS_PER_SEC;
std::cout << time << std::endl;
return 0;
}
void* avx_min_t(void* input)
不会对min
执行任何操作,因此从阵列加载 SIMD 工作负载会得到优化。
它的内循环用gcc -O3 -march=haswell
编译到这个,和 clang 基本相同。
.L3:
add ebx, r12d
cmp r13d, ebx
jg .L3
所以它实际上只是 asm 中的一个空循环,需要 0.04 秒才能将指针增加4GB / 32 bytes
倍。
for(int i = start ; i < rel_size ; i += offset){
}
我认为你的意思是返回一些东西,因为该函数被声明为void*
并且具有从非 void 函数末尾掉落的未定义行为。GCC 和 clang 默认警告这一点,甚至不需要-Wall
. https://godbolt.org/z/Z1GWpU
<source>: In function 'void* avx_min_t(void*)':
<source>:66:1: warning: no return statement in function returning non-void [-Wreturn-type]
66 | }
| ^
始终检查编译器警告,尤其是当代码行为异常时。启用-Wall
并修复任何警告。
- 如何为一段代码启用 -permissive
- 使用Qt Creator在调试模式下编译一段代码
- 无法理解一段具有完美转发和省略号的C++代码
- 防止一段代码在协程中并发执行
- 通过创建单个线程来运行一段代码可加快执行速度
- (C++)比较两段代码,一段有效,一段无效,无法找出区别
- 有没有一种标准方法来确保一段代码在全局范围内执行
- C++函数 Sleep() 在一段代码之前执行
- C++中的继承和模板:为什么以下一段代码不编译?
- 一段C++代码出现错误"invalid pointer"
- 我必须编写一段代码,基本上执行c++中的strlen()函数,哪里出了问题
- 关于行列式计算的一段代码的问题
- 将两个或多个互斥体应用于一段代码
- 我应该如何处理编写一段确定订阅包节省的代码的逻辑
- 使类使用浮点数和整数(分析一段代码)
- 如何在C++中编写一段代码以查找系统路径上文件的完整路径
- 有关一段带有模板、转换运算符和复制 ctor 的代码的问题
- 从第二次调用开始在函数中执行一段代码
- 将一段C++代码封装到Python中
- 解释一段c++代码