Telegram Group & Telegram Channel
#dev #cpp #rust

Есть баян, что вот в таком коде функция в качестве коллбека менее эффективна, чем лямбда:

#include <cstdio>

__attribute__((noinline))
void invoke_callback(auto callback) {
callback();
}

void my_callback() {
puts("invoke via function");
}

int main() {
invoke_callback(my_callback);

invoke_callback([]() {
puts("invoke via lambda");
});
}


Ларчик открывается просто: my_callack мгновенно превращается в указатель на функцию, и invoke_callback приходится делать непрямой вызов по указателю. Лямбда же является уникальным типом с фиксированным перегруженным operator(), поэтому коллбек спокойно инлайнится. Не менее баян, что в Rust это не проблема, и

#[inline(never)]
fn invoke_callback(callback: impl FnOnce()) {
callback();
}

fn my_callback() {
println!("invoke via function");
}

#[no_mangle]
fn main() {
invoke_callback(my_callback);

invoke_callback(|| {
println!("invoke via lambda");
});
}


работает эффективно в обоих случаях, ведь в Rust функции являются не указателями, а анонимными ZST-типами, прям как лямбды. А вот вам не баян: оказывается, если заменить вызов invoke_callback на invoke_callback(my_callback as fn()), чтобы произошел вызов по указателю, производительность не ухудшится! Почему?

Здесь происходит нечто в духе девиртуализации. При интер-процедурном анализе компилятор видит, что аргумент invoke_callback<fn()> — это всегда указатель на my_callback, поэтому callback() можно оптимизировать до my_callback().

Почему Clang не может сделать то же самое, ведь он тоже использует LLVM? Может, если invoke_callback сделать статической функцией (GCC тоже делает девиртуализацию, но потом не инлайнит вызов). Функции без static же видны из других единицах трансляции, поэтому вполне возможно, что кто-нибудь "снаружи" вызовет invoke_callback<void (*)()> с аргументом помимо my_callback, и в этом случае оптимизация неприменима.

Естественно, как только начинают использоваться два разных коллбека, девиртуализация ломается, так что использовать ZST-типы вместо указателей на функции все еще полезно.

Забавно, что даже то, что invoke_callback — шаблонная функция, не помогает, хотя, казалось бы, раз каждый cpp-файл #include'ит в себя заголовок с реализацией шаблонной функции, то проводить оптимизацию в рамках единицы трансляции корректно. Это не так по двум причинам.

Во-первых, см. пост выше: inline-функции пусть и компилируются несколько раз, но потом дедублицируются, т.е. код, сгенерированной каждой единицей трансляции, должен быть валиден для вызовов из других единиц трансляции.

Во-вторых, шаблонная функция может быть определена в одной единице трансляции, а затем использоваться в другой без реализации:

// a.cpp
#include <iostream>

template<typename T>
void print(T value) {
std::cout << value;
}

template void print<int>(int value);
template void print<const char*>(const char* value);

// main.cpp
template<typename T>
void print(T value);

int main() {
print(2);
print(" + ");
print(2);
print(" = ");
print(4);
print("\n");
}


Конструкция с template без угловых скобок инстанциирует шаблонную функцию для данного T, и другие единицы линковки потом могут к этим инстансам линковаться. (Попытка использовать в main.cpp print с неинстанциированными типами при этом приведет к ошибке линковки.)



group-telegram.com/alisa_rummages/206
Create:
Last Update:

#dev #cpp #rust

Есть баян, что вот в таком коде функция в качестве коллбека менее эффективна, чем лямбда:

#include <cstdio>

__attribute__((noinline))
void invoke_callback(auto callback) {
callback();
}

void my_callback() {
puts("invoke via function");
}

int main() {
invoke_callback(my_callback);

invoke_callback([]() {
puts("invoke via lambda");
});
}


Ларчик открывается просто: my_callack мгновенно превращается в указатель на функцию, и invoke_callback приходится делать непрямой вызов по указателю. Лямбда же является уникальным типом с фиксированным перегруженным operator(), поэтому коллбек спокойно инлайнится. Не менее баян, что в Rust это не проблема, и

#[inline(never)]
fn invoke_callback(callback: impl FnOnce()) {
callback();
}

fn my_callback() {
println!("invoke via function");
}

#[no_mangle]
fn main() {
invoke_callback(my_callback);

invoke_callback(|| {
println!("invoke via lambda");
});
}


работает эффективно в обоих случаях, ведь в Rust функции являются не указателями, а анонимными ZST-типами, прям как лямбды. А вот вам не баян: оказывается, если заменить вызов invoke_callback на invoke_callback(my_callback as fn()), чтобы произошел вызов по указателю, производительность не ухудшится! Почему?

Здесь происходит нечто в духе девиртуализации. При интер-процедурном анализе компилятор видит, что аргумент invoke_callback<fn()> — это всегда указатель на my_callback, поэтому callback() можно оптимизировать до my_callback().

Почему Clang не может сделать то же самое, ведь он тоже использует LLVM? Может, если invoke_callback сделать статической функцией (GCC тоже делает девиртуализацию, но потом не инлайнит вызов). Функции без static же видны из других единицах трансляции, поэтому вполне возможно, что кто-нибудь "снаружи" вызовет invoke_callback<void (*)()> с аргументом помимо my_callback, и в этом случае оптимизация неприменима.

Естественно, как только начинают использоваться два разных коллбека, девиртуализация ломается, так что использовать ZST-типы вместо указателей на функции все еще полезно.

Забавно, что даже то, что invoke_callback — шаблонная функция, не помогает, хотя, казалось бы, раз каждый cpp-файл #include'ит в себя заголовок с реализацией шаблонной функции, то проводить оптимизацию в рамках единицы трансляции корректно. Это не так по двум причинам.

Во-первых, см. пост выше: inline-функции пусть и компилируются несколько раз, но потом дедублицируются, т.е. код, сгенерированной каждой единицей трансляции, должен быть валиден для вызовов из других единиц трансляции.

Во-вторых, шаблонная функция может быть определена в одной единице трансляции, а затем использоваться в другой без реализации:

// a.cpp
#include <iostream>

template<typename T>
void print(T value) {
std::cout << value;
}

template void print<int>(int value);
template void print<const char*>(const char* value);

// main.cpp
template<typename T>
void print(T value);

int main() {
print(2);
print(" + ");
print(2);
print(" = ");
print(4);
print("\n");
}


Конструкция с template без угловых скобок инстанциирует шаблонную функцию для данного T, и другие единицы линковки потом могут к этим инстансам линковаться. (Попытка использовать в main.cpp print с неинстанциированными типами при этом приведет к ошибке линковки.)

BY Алиса копается


Warning: Undefined variable $i in /var/www/group-telegram/post.php on line 260

Share with your friend now:
group-telegram.com/alisa_rummages/206

View MORE
Open in Telegram


Telegram | DID YOU KNOW?

Date: |

As a result, the pandemic saw many newcomers to Telegram, including prominent anti-vaccine activists who used the app's hands-off approach to share false information on shots, a study from the Institute for Strategic Dialogue shows. The last couple days have exemplified that uncertainty. On Thursday, news emerged that talks in Turkey between the Russia and Ukraine yielded no positive result. But on Friday, Reuters reported that Russian President Vladimir Putin said there had been some “positive shifts” in talks between the two sides. After fleeing Russia, the brothers founded Telegram as a way to communicate outside the Kremlin's orbit. They now run it from Dubai, and Pavel Durov says it has more than 500 million monthly active users. This ability to mix the public and the private, as well as the ability to use bots to engage with users has proved to be problematic. In early 2021, a database selling phone numbers pulled from Facebook was selling numbers for $20 per lookup. Similarly, security researchers found a network of deepfake bots on the platform that were generating images of people submitted by users to create non-consensual imagery, some of which involved children. He said that since his platform does not have the capacity to check all channels, it may restrict some in Russia and Ukraine "for the duration of the conflict," but then reversed course hours later after many users complained that Telegram was an important source of information.
from us


Telegram Алиса копается
FROM American