Мой ответ основан на том, как работает динамический компоновщик Linux / glibc / ELF, но я бы предположил, что общий ответ такой же для других платформ:
Существует разница между первым вызовом динамически загруженного символа и последующими вызовами. Первый звонок дорогой, может включать много циклов. Все остальные вызовы более или менее на 1 - 2 инструкции.
Способ, которым он работает, заключается в том, что компоновщик настраивает запись в таблице связей процедур, которая извлекает адрес для этой внешней функции из таблицы глобальных смещений. При первом вызове адрес GOT указывает на заглушку, которая запускает динамический компоновщик для разрешения реального адреса функции в DLL. Это может занять много циклов, но как только это будет сделано один раз, динамический компоновщик направит запись GOT, чтобы указать непосредственно на функцию, поэтому при следующем вызове PLT-кода вызов будет напрямую к функции.
Вот ссылка на довольно хороший обзор этого процесса: http://www.technovelty.org/linux/pltgot.html