调度SIMD指令+ SIMDPP + qmake

Dispatching SIMD instructions + SIMDPP + qmake

本文关键字:SIMDPP qmake 指令 SIMD 调度      更新时间:2023-10-16

我正在开发一个使用SIMD指令集的QT小部件。我已经编译了3个版本:SSE3, AVX和AVX2(simdpp允许通过单个#define在它们之间切换)。

现在,我想让我的小部件根据最佳支持的指令集在这些实现之间自动切换。simdpp提供的指南使用了一些makefile魔法:

CXXFLAGS=""
test: main.o test_sse2.o test_sse3.o test_sse4_1.o test_null.o
    g++ $^ -o test
main.o: main.cc
    g++ main.cc $(CXXFLAGS) -c -o main.o
test_null.o: test.cc
    g++ test.cc -c $(CXXFLAGS) -DSIMDPP_EMIT_DISPATCHER 
        -DSIMDPP_DISPATCH_ARCH1=SIMDPP_ARCH_X86_SSE2 
        -DSIMDPP_DISPATCH_ARCH2=SIMDPP_ARCH_X86_SSE3 
        -DSIMDPP_DISPATCH_ARCH3=SIMDPP_ARCH_X86_SSE4_1 -o test_null.o
test_sse2.o: test.cc
    g++ test.cc -c $(CXXFLAGS) -DSIMDPP_ARCH_X86_SSE2 -msse2 -o test_sse2.o
test_sse3.o: test.cc
    g++ test.cc -c $(CXXFLAGS) -DSIMDPP_ARCH_X86_SSE3 -msse3 -o test_sse3.o
test_sse4_1.o: test.cc
    g++ test.cc -c $(CXXFLAGS) -DSIMDPP_ARCH_X86_SSE4_1 -msse4.1 -o test_sse4_1.o

这里是指南的链接:http://p12tic.github.io/libsimdpp/v2.0~rc2/libsimdpp/arch/dispatch.html

我不知道如何用qmake实现这样的行为。什么好主意吗?

首先想到的是创建一个带有分派代码的共享库,并将其链接到项目。我又被困住了。App是跨平台的,这意味着它必须同时使用GCC和MSVC(确切地说,是vc120)进行编译,这迫使我在Windows中使用nmake,我试过了,真的,但这是我整个程序员生涯中最糟糕的经历。

提前感谢全世界的程序员!

很抱歉,这有点晚了。希望我还能帮上忙。

你需要考虑两个方面:编译时和运行时。

编译时间-需要创建代码来支持不同的特性。运行时——需要创建代码来决定哪些特性可以运行。

你要做的是创建一个调度程序…

FuncImpl.h:

#pragma once
void execAvx2();
void execAvx();
void execSse();
void execDefault();

FuncImpl.cpp:

// Compile this file once for each variant with different compiler settings.
#if defined(__AVX2__)
void execAvx2()
{
 // AVX2 impl
...
}
#elif defined (__AVX__)
void execAvx()
{
// AVX impl
...
}
#elif defined (__SSE4_2__)
void execSse()
{
 // Sse impl
...
}
#else
void execDefault()
{
 // Vanilla impl
...
}
#endif

DispatchFunc.cpp

#include "FuncImpl.h"
// Decide at runtime which code to run
void dispatchFunc()
{
     if(CheckCpuAvx2Flag())
     {
         execAvx2();
     } 
     else if(CheckCpuAvxFlag())
     {
         execAvx();
     }
     else if(CheckCpuSseFlags())
     {
         execSse();
     }
     else
     {
         execDefault();
     }
}

你能做的就是创建一个QMAKE_EXTRA_COMPILERS的集合。

SampleCompiler。pri(对每个变体执行此操作):

MyCompiler.name = MyCompiler         # Name
MyCompiler.input = MY_SOURCES        # Symbol of the source list to compile
MyCompiler.dependency_type = TYPE_C
MyCompiler.variable_out = OBJECTS
# EXTRA_CXXFLAGS = -mavx / -mavx2 / -msse4.2
# _var = creates FileName_var.o => replace with own variant (_sse, etc)  
MyCompiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}_var$${first(QMAKE_EXT_OBJ)}
MyCompiler.commands = $${QMAKE_CXX} $(CXXFLAGS) $${EXTRA_CXXFLAGS} $(INCPATH) -c ${QMAKE_FILE_IN} -o${QMAKE_FILE_OUT}
QMAKE_EXTRA_COMPILERS += MyCompiler   # Add my compiler

MyProject.pro

...
include(SseCompiler.pri)
include(AvxCompiler.pri)
include(Avx2Compiler.pri)
..
# Normal sources
# Will create FuncImpl.o and DispatchFunc.o
SOURCES += FuncImpl.cpp 
           DispatchFunc.cpp
# Give the other compilers their sources
# Will create FuncImpl_avx2.o FuncImpl_avx.o FuncImpl_sse.o
AVX2_SOURCES += FuncImpl.cpp
AVX_SOURCES += FuncImpl.cpp
SSE_SOURCES += FuncImpl.cpp
# Link all objects
...

现在只需要调用dispatchFunc()!

检查cpu标志是另一个练习:cpuid

这些只是项目定义。在.pro文件中使用definitions +=设置它们。您可以为想要支持的指令集设置标志,simdpp会在运行时为处理器选择最佳的指令集。

例如,添加一个定义到qmake与一个值?

这是一个用于SIMD调度程序的qmake .pro文件。这是相当冗长的,所以对于更多的指令集,最好是通过脚本生成分派块,将其写入.pri文件,然后将其包含在主.pro文件中。

TEMPLATE = app
TARGET = simd_test
INCLUDEPATH += .
QMAKE_CXXFLAGS = -O3 -std=c++17
SOURCES += main.cpp
SOURCES_dispatch = test.cpp
{
    # SSE2
    DISPATCH_CXXFLAGS = -msse2
    DISPATCH_SUFFIX = _sse2
    src_dispatch_sse2.name = src_dispatch_sse2
    src_dispatch_sse2.input = SOURCES_dispatch
    src_dispatch_sse2.dependency_type = TYPE_C
    src_dispatch_sse2.variable_out = OBJECTS
    src_dispatch_sse2.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${DISPATCH_SUFFIX}$${first(QMAKE_EXT_OBJ)}
    src_dispatch_sse2.commands = $${QMAKE_CXX} $(CXXFLAGS) $${DISPATCH_CXXFLAGS} $(INCPATH) -c ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
    QMAKE_EXTRA_COMPILERS += src_dispatch_sse2
}
{
    # SSE3
    DISPATCH_CXXFLAGS = -msse3
    DISPATCH_SUFFIX = _sse3
    src_dispatch_sse3.name = src_dispatch_sse3
    src_dispatch_sse3.input = SOURCES_dispatch
    src_dispatch_sse3.dependency_type = TYPE_C
    src_dispatch_sse3.variable_out = OBJECTS
    src_dispatch_sse3.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${DISPATCH_SUFFIX}$${first(QMAKE_EXT_OBJ)}
    src_dispatch_sse3.commands = $${QMAKE_CXX} $(CXXFLAGS) $${DISPATCH_CXXFLAGS} $(INCPATH) -c ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
    QMAKE_EXTRA_COMPILERS += src_dispatch_sse3
}
{
    # SSE41
    DISPATCH_CXXFLAGS = -msse4.1
    DISPATCH_SUFFIX = _sse41
    src_dispatch_sse41.name = src_dispatch_sse41
    src_dispatch_sse41.input = SOURCES_dispatch
    src_dispatch_sse41.dependency_type = TYPE_C
    src_dispatch_sse41.variable_out = OBJECTS
    src_dispatch_sse41.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_IN_BASE}$${DISPATCH_SUFFIX}$${first(QMAKE_EXT_OBJ)}
    src_dispatch_sse41.commands = $${QMAKE_CXX} $(CXXFLAGS) $${DISPATCH_CXXFLAGS} $(INCPATH) -c ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
    QMAKE_EXTRA_COMPILERS += src_dispatch_sse41
}