Я пишу библиотечную функцию, скажем, count_char(const char *str, int len, char ch)
, которая обнаруживает поддерживаемые расширения SIMD процессора, на котором он работает, и отправляет вызов, скажем, в версию, оптимизированную для AVX2 или SSE4.2. Поскольку я хотел бы избежать штрафа за выполнение пары cpuid
инструкций для каждого вызова, я пытаюсь сделать это только один раз при первом вызове функции (которая может вызываться разными потоками одновременно).
На земле C ++ я бы просто сделал что-то вроде
int count_char(const char *str, int len, char ch) {
static const auto fun_ptr = select_simd_function();
return (*fun_ptr)(str, len, ch);
}
и полагался на семантику C ++ static
, чтобы гарантировать, что он вызывается ровно один раз без каких-либо условий гонки. Но какой лучший способ сделать это в чистом виде C?
Вот что я придумал:
- Использование атомов c переменных (которые также присутствуют в C) - довольно подвержен ошибкам и немного сложнее в обслуживании.
- Использование
pthread_once
- не уверен в том, какие накладные расходы у него есть, плюс это может вызвать головную боль при Windows. - принуждение пользователя библиотеки вызывать другую библиотечную функцию для инициализации указателя - короче говоря, в моем случае это не сработает, поскольку на самом деле это C бит библиотеки для другого языка.
- выравнивание указатель на 8 байтов и полагается на то, что x86-доступ к файлу размером атома c - непереносим для других архитектур (позже я буду реализовывать некоторые версии PowerP C или ARM-специфицированные c SIMD, скажем), технически UB (по крайней мере в C ++).
- Использование локального потокового хранилища и маркировка
fun_ptr
как thread_local
, а затем что-то вроде
static thread_local fun_ptr_t fun_ptr = NULL;
if (!fun_ptr) {
fun_ptr = select_simd_function();
}
return (*fun_ptr)(str, len, ch);
Преимущество в том, что код очень ясен и, видимо, правильно, но я не с о влиянии TLS на производительность, плюс каждый поток должен будет вызвать select_simd_function()
один раз (но это, вероятно, не имеет большого значения).
Лично для меня (5) пока победитель, за которым внимательно следят by (1) (я бы, вероятно, даже go с (1), если бы это была не чья-то основополагающая библиотека, и я не хотел бы смущать себя ошибочной реализацией).
Так Какой будет лучший вариант? Я что-то пропустил?