Без корневого доступа, запустите R с настроенным BLAS, когда он связан с эталонным BLAS
Может кто-нибудь сказать мне, почему я не могу успешно протестировать OpenBLAS dgemm
производительность (в GFLOP) в R следующим образом?
- ссылка R с "ссылкой BLAS"
libblas.so
- скомпилировать мою C программу
mmperf.c
с библиотекой OpenBLASlibopenblas.so
- загрузить получившуюся общую библиотеку
mmperf.so
в R, вызвать функцию оболочки Rmmperf
и сообщитьdgemm
производительность в GFLOP.
Точка 1 выглядит странно, но у меня нет выбора, потому что у меня нет доступа с правами root на машинах, которые я хочу протестировать, поэтому фактическое соединение с OpenBLAS невозможно. Под "не успешно" я имею в виду, что моя программа в конечном итоге сообщает dgemm
производительность для справки BLAS вместо OpenBLAS. Я надеюсь, что кто-то может объяснить мне:
- почему мой путь не работает;
- Можно ли вообще заставить его работать (это важно, потому что, если это невозможно, я должен написать C
main
работать и выполнять свою работу в программе на Си.)
Я исследовал эту проблему в течение двух дней, здесь я включу различные выходные данные системы, чтобы помочь вам поставить диагноз. Чтобы сделать вещи воспроизводимыми, я также включу код, makefile и команду shell.
Часть 1: системная среда перед тестированием
Есть 2 способа вызвать R, используя R
или же Rscript
, Есть некоторые различия в том, что загружается при их вызове:
~/Desktop/dgemm$ readelf -d $(R RHOME)/bin/exec/R | grep "NEEDED"
0x00000001 (NEEDED) Shared library: [libR.so]
0x00000001 (NEEDED) Shared library: [libpthread.so.0]
0x00000001 (NEEDED) Shared library: [libc.so.6]
~/Desktop/dgemm$ readelf -d $(R RHOME)/bin/Rscript | grep "NEEDED"
0x00000001 (NEEDED) Shared library: [libc.so.6]
Здесь нам нужно выбрать Rscript
, так как R
грузы libR.so
, который автоматически загрузит ссылку BLAS libblas.so.3
:
~/Desktop/dgemm$ readelf -d $(R RHOME)/lib/libR.so | grep blas
0x00000001 (NEEDED) Shared library: [libblas.so.3]
~/Desktop/dgemm$ ls -l /etc/alternatives/libblas.so.3
... 31 May /etc/alternatives/libblas.so.3 -> /usr/lib/libblas/libblas.so.3.0
~/Desktop/dgemm$ readelf -d /usr/lib/libblas/libblas.so.3 | grep SONAME
0x0000000e (SONAME) Library soname: [libblas.so.3]
Сравнительно, Rscript
дает более чистую окружающую среду.
Часть 2: OpenBLAS
После загрузки исходного файла из OpenBLAS и простого make
команда, общая библиотека вида libopenblas-<arch>-<release>.so-<version>
может быть сгенерировано. Обратите внимание, что у нас не будет root-прав для его установки; вместо этого мы копируем эту библиотеку в наш рабочий каталог ~/Desktop/dgemm
и переименовать его просто libopenblas.so
, В то же время мы должны сделать еще одну копию с именем libopenblas.so.0
, поскольку это SONAME, который загрузчик времени выполнения будет искать:
~/Desktop/dgemm$ readelf -d libopenblas.so | grep "RPATH\|SONAME"
0x0000000e (SONAME) Library soname: [libopenblas.so.0]
Обратите внимание, что RPATH
атрибут не указан, что означает, что эта библиотека предназначена для /usr/lib
и мы должны позвонить ldconfig
добавить его в ld.so.cache
, Но опять же у нас нет root-прав для этого. На самом деле, если это можно сделать, то все трудности ушли. Мы могли бы тогда использовать update-alternatives --config libblas.so.3
эффективно связать R с OpenBLAS.
Часть 3: C-код, Makefile и R-код
Вот сценарий C mmperf.c
вычисление GFLOP умножения 2 квадратных матриц размера N
:
#include <R.h>
#include <Rmath.h>
#include <Rinternals.h>
#include <R_ext/BLAS.h>
#include <sys/time.h>
/* standard C subroutine */
double mmperf (int n) {
/* local vars */
int n2 = n * n, tmp; double *A, *C, one = 1.0;
struct timeval t1, t2; double elapsedTime, GFLOPs;
/* simulate N-by-N matrix A */
A = (double *)calloc(n2, sizeof(double));
GetRNGstate();
tmp = 0; while (tmp < n2) {A[tmp] = runif(0.0, 1.0); tmp++;}
PutRNGstate();
/* generate N-by-N zero matrix C */
C = (double *)calloc(n2, sizeof(double));
/* time 'dgemm.f' for C <- A * A + C */
gettimeofday(&t1, NULL);
F77_CALL(dgemm) ("N", "N", &n, &n, &n, &one, A, &n, A, &n, &one, C, &n);
gettimeofday(&t2, NULL);
/* free memory */
free(A); free(C);
/* compute and return elapsedTime in microseconds (usec or 1e-6 sec) */
elapsedTime = (double)(t2.tv_sec - t1.tv_sec) * 1e+6;
elapsedTime += (double)(t2.tv_usec - t1.tv_usec);
/* convert microseconds to nanoseconds (1e-9 sec) */
elapsedTime *= 1e+3;
/* compute and return GFLOPs */
GFLOPs = 2.0 * (double)n2 * (double)n / elapsedTime;
return GFLOPs;
}
/* R wrapper */
SEXP R_mmperf (SEXP n) {
double GFLOPs = mmperf(asInteger(n));
return ScalarReal(GFLOPs);
}
Вот простой скрипт R mmperf.R
сообщить GFLOPs для случая N = 2000
mmperf <- function (n) {
dyn.load("mmperf.so")
GFLOPs <- .Call("R_mmperf", n)
dyn.unload("mmperf.so")
return(GFLOPs)
}
GFLOPs <- round(mmperf(2000), 2)
cat(paste("GFLOPs =",GFLOPs, "\n"))
Наконец, есть простой make-файл для генерации разделяемой библиотеки. mmperf.so
:
mmperf.so: mmperf.o
gcc -shared -L$(shell pwd) -Wl,-rpath=$(shell pwd) -o mmperf.so mmperf.o -lopenblas
mmperf.o: mmperf.c
gcc -fpic -O2 -I$(shell Rscript --default-packages=base --vanilla -e 'cat(R.home("include"))') -c mmperf.c
Поместите все эти файлы в рабочий каталог ~/Desktop/dgemm
и скомпилируйте его:
~/Desktop/dgemm$ make
~/Desktop/dgemm$ readelf -d mmperf.so | grep "NEEDED\|RPATH\|SONAME"
0x00000001 (NEEDED) Shared library: [libopenblas.so.0]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000f (RPATH) Library rpath: [/home/zheyuan/Desktop/dgemm]
Выходные данные заверяют нас, что OpenBLAS правильно связан, и путь загрузки во время выполнения установлен правильно.
Часть 4: тестирование OpenBLAS в R
Давайте сделаем
~/Desktop/dgemm$ Rscript --default-packages=base --vanilla mmperf.R
Обратите внимание, что наш скрипт требует только base
пакет в R, и --vanilla
используется для игнорирования всех пользовательских настроек при запуске R. На моем ноутбуке возвращается моя программа:
GFLOPs = 1.11
К сожалению! Это действительно эталонная производительность BLAS, а не OpenBLAS (что составляет около 8-9 GFLOP).
Часть 5: почему?
Если честно, я не знаю, почему это происходит. Кажется, каждый шаг работает правильно. Происходит ли что-то тонкое, когда вызывается R? Например, есть ли вероятность того, что библиотека OpenBLAS будет переопределена ссылкой BLAS в какой-то момент по какой-то причине? Есть объяснения и решения? Спасибо!
3 ответа
почему мой путь не работает
Во-первых, разделяемые библиотеки в UNIX предназначены для имитации работы архивных библиотек (сначала там были архивные библиотеки). В частности это означает, что если у вас есть libfoo.so
а также libbar.so
оба определяющих символа foo
то, какая библиотека загружается первой, выигрывает: все ссылки на foo
из любой точки программы (в том числе из libbar.so
) будет привязан к libfoo.so
определение foo
,
Это имитирует то, что произошло бы, если бы вы связали свою программу с libfoo.a
а также libbar.a
где обе архивные библиотеки определяют один и тот же символ foo
, Более подробную информацию о архиве ссылки здесь.
Сверху должно быть ясно, что если libblas.so.3
а также libopenblas.so.0
определить тот же набор символов (что они делают), и если libblas.so.3
сначала загружается в процесс, затем из libopenblas.so.0
никогда не будет называться.
Во-вторых, вы правильно решили, что с R
напрямую ссылается на libR.so
, и с тех пор libR.so
напрямую ссылается на libblas.so.3
Гарантируется, что libopenblas.so.0
проиграет битву.
Однако вы ошибочно решили, что Rscript
лучше, но это не так Rscript
крошечный двоичный файл (11 КБ в моей системе; сравните с 2,4 МБ для libR.so
), и примерно все, что он делает, это exec
из R
, Это тривиально видеть в strace
выход:
strace -e trace=execve /usr/bin/Rscript --default-packages=base --vanilla /dev/null
execve("/usr/bin/Rscript", ["/usr/bin/Rscript", "--default-packages=base", "--vanilla", "/dev/null"], [/* 42 vars */]) = 0
execve("/usr/lib/R/bin/R", ["/usr/lib/R/bin/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null", "--args"], [/* 43 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89625, si_status=0, si_utime=0, si_stime=0} ---
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89626, si_status=0, si_utime=0, si_stime=0} ---
execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null", "--args"], [/* 51 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=89630, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
Это означает, что к тому времени, когда ваш скрипт начнет выполняться, libblas.so.3
был загружен, и libopenblas.so.0
который будет загружен как зависимость mmperf.so
на самом деле не будет использоваться ни для чего.
можно ли вообще заставить это работать
Наверное. Я могу придумать два возможных решения:
- Притворись, что
libopenblas.so.0
на самом делеlibblas.so.3
- Перестроить весь
R
пакет противlibopenblas.so
,
Для #1 вам нужно ln -s libopenblas.so.0 libblas.so.3
затем убедитесь, что ваша копия libblas.so.3
находится до системного, путем установки LD_LIBRARY_PATH
соответственно.
Похоже, это работает для меня:
mkdir /tmp/libblas
# pretend that libc.so.6 is really libblas.so.3
cp /lib/x86_64-linux-gnu/libc.so.6 /tmp/libblas/libblas.so.3
LD_LIBRARY_PATH=/tmp/libblas /usr/bin/Rscript /dev/null
Error in dyn.load(file, DLLpath = DLLpath, ...) :
unable to load shared object '/usr/lib/R/library/stats/libs/stats.so':
/usr/lib/liblapack.so.3: undefined symbol: cgemv_
During startup - Warning message:
package ‘stats’ in options("defaultPackages") was not found
Обратите внимание, как я получил ошибку (мой "притворяться" libblas.so.3
не определяет символы, ожидаемые от него, так как это действительно копия libc.so.6
).
Вы также можете подтвердить, какая версия libblas.so.3
загружается таким образом:
LD_DEBUG=libs LD_LIBRARY_PATH=/tmp/libblas /usr/bin/Rscript /dev/null |& grep 'libblas\.so\.3'
91533: find library=libblas.so.3 [0]; searching
91533: trying file=/usr/lib/R/lib/libblas.so.3
91533: trying file=/usr/lib/x86_64-linux-gnu/libblas.so.3
91533: trying file=/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server/libblas.so.3
91533: trying file=/tmp/libblas/libblas.so.3
91533: calling init: /tmp/libblas/libblas.so.3
Для #2 вы сказали:
У меня нет доступа с правами root на машинах, которые я хочу протестировать, поэтому фактическая ссылка на OpenBLAS невозможна.
но это кажется ложным аргументом: если вы можете построить libopenblas
Конечно, вы также можете построить свою собственную версию R
,
Обновить:
В начале вы упомянули, что libblas.so.3 и libopenblas.so.0 определяют один и тот же символ, что это значит? У них разные SONAME, этого недостаточно, чтобы отличить их по системе?
Символы и SONAME
не имеют ничего общего друг с другом.
Вы можете увидеть символы на выходе из readelf -Ws libblas.so.3
а также readelf -Ws libopenblas.so.0
, Символы, связанные с BLAS
, такие как cgemv_
, появится в обеих библиотеках.
Ваше замешательство по поводу SONAME
возможно происходит из Windows. DLL
S на Windows разработаны совершенно по-другому. В частности, когда FOO.DLL
символ импорта bar
от BAR.DLL
, оба название символа (bar
) и DLL
из которого этот символ был импортирован (BAR.DLL
) записаны в FOO.DLL
Таблица импорта.
Это позволяет легко иметь R
Импортировать cgemv_
от BLAS.DLL
, в то время как MMPERF.DLL
импортирует тот же символ из OPENBLAS.DLL
,
Однако это затрудняет вставку библиотеки и работает совершенно иначе, чем работа архивных библиотек (даже в Windows).
Мнения расходятся в том, какой дизайн лучше в целом, но ни одна из систем, вероятно, никогда не изменит свою модель.
В UNIX есть способы эмулировать привязку символов в стиле Windows: см. RTLD_DEEPBIND
в справочной странице dlopen. Осторожно: это чревато опасностями, которые могут запутать экспертов по UNIX, не используются широко и могут иметь ошибки при реализации.
Обновление 2:
ты имеешь в виду, что я компилирую R и устанавливаю его в своем домашнем каталоге?
Да.
Затем, когда я хочу вызвать его, я должен явно указать путь к моей версии исполняемой программы, в противном случае вместо этого может быть вызван тот, что в системе? Или я могу поместить этот путь в первую позицию переменной среды $PATH, чтобы обмануть систему?
В любом случае работает.
*********************
Решение 2:
*********************
Здесь мы предлагаем другое решение, используя переменную среды LD_PRELOAD
упоминается в нашем решении 1. Использование LD_PRELOAD
является более "жестоким", так как он заставляет загружать данную библиотеку в программу перед любой другой программой, даже перед библиотекой C libc.so
! Это часто используется для срочного исправления в разработке Linux.
Как показано во второй части оригинального сообщения, общая библиотека BLAS libopenblas.so
имеет SONAME libopenblas.so.0
, SONAME - это внутреннее имя, которое динамический библиотечный загрузчик будет искать во время выполнения, поэтому нам нужно сделать символическую ссылку на libopenblas.so
с этим SONAME:
~/Desktop/dgemm$ ln -sf libopenblas.so libopenblas.so.0
тогда мы экспортируем это:
~/Desktop/dgemm$ export LD_PRELOAD=$(pwd)/libopenblas.so.0
Обратите внимание, что полный путь к libopenblas.so.0
нужно кормить LD_PRELOAD
для успешной загрузки, даже если libopenblas.so.0
находится под $(pwd)
,
Теперь мы запускаем Rscript
и проверить, что происходит LD_DEBUG
:
~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4865: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4868: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
4870: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4869: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4867: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
4860: find library=libblas.so.3 [0]; searching
4860: trying file=/usr/lib/R/lib/libblas.so.3
4860: trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
4860: trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
4860: trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
4860: trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
4860: trying file=/usr/lib/i386-linux-gnu/libblas.so.3
4860: trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
4860: trying file=/usr/lib/libblas.so.3
4860: calling init: /usr/lib/libblas.so.3
4860: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4874: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4876: calling init: /home/zheyuan/Desktop/dgemm/libopenblas.so
4860: calling fini: /home/zheyuan/Desktop/dgemm/libopenblas.so [0]
4860: calling fini: /usr/lib/libblas.so.3 [0]
Сравнивая с тем, что мы видели в решении 1, обманывая R с нашей собственной версией libblas.so.3
, мы это видим
libopenblas.so.0
загружается первым, следовательно, сначалаRscript
;- после
libopenblas.so.0
найден,Rscript
продолжает поиск и загрузкуlibblas.so.3
, Однако это не повлияет на правило "первым пришел, первым обслужен", объясненное в исходном ответе.
Хорошо, все работает, поэтому мы проверяем наши mmperf.c
программа:
~/Desktop/dgemm$ Rscript --default-packages=base --vanilla mmperf.R
GFLOPs = 9.62
Результат 9,62 больше, чем 8,77, который мы видели в предыдущем решении просто случайно. В качестве теста для использования OpenBLAS мы не проводим эксперимент много раз для получения более точного результата.
Затем, как обычно, мы сбрасываем переменную окружения в конце:
~/Desktop/dgemm$ unset LD_PRELOAD
*********************
Решение 1:
*********************
Благодаря Employed Russian моя проблема наконец-то решена. Расследование требует важных навыков в отладке и исправлении системы Linux, и я считаю, что это большой опыт, который я усвоил. Здесь я хотел бы опубликовать решение, а также исправить несколько пунктов в моем исходном сообщении.
1 О вызове R
В моем оригинальном сообщении я упоминал, что есть два способа запустить R, либо через R
или же Rscript
, Однако я неправильно преувеличивал их разницу. Давайте теперь исследуем их процесс запуска с помощью важного средства отладки Linux strace
(увидеть man strace
). На самом деле после ввода команды в оболочке происходит множество интересных вещей, и мы можем использовать
strace -e trace=process [command]
отслеживать все системные вызовы, связанные с управлением процессами. В результате мы можем наблюдать за этапами обработки, ожидания и выполнения процесса. @Employed Russian, хотя и не указано на странице руководства, показывает, что можно указать только подкласс process
, например, execve
для выполнения шагов.
За R
у нас есть
~/Desktop/dgemm$ time strace -e trace=execve R --vanilla < /dev/null > /dev/null
execve("/usr/bin/R", ["R", "--vanilla"], [/* 70 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5777, si_status=0, si_utime=0, si_stime=0} ---
execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--vanilla"], [/* 79 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5778, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
real 0m0.345s
user 0m0.256s
sys 0m0.068s
в то время как для Rscript
у нас есть
~/Desktop/dgemm$ time strace -e trace=execve Rscript --default-packages=base --vanilla /dev/null
execve("/usr/bin/Rscript", ["Rscript", "--default-packages=base", "--vanilla", "/dev/null"], [/* 70 vars */]) = 0
execve("/usr/lib/R/bin/R", ["/usr/lib/R/bin/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null"], [/* 71 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5822, si_status=0, si_utime=0, si_stime=0} ---
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5823, si_status=0, si_utime=0, si_stime=0} ---
execve("/usr/lib/R/bin/exec/R", ["/usr/lib/R/bin/exec/R", "--slave", "--no-restore", "--vanilla", "--file=/dev/null"], [/* 80 vars */]) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=5827, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
real 0m0.063s
user 0m0.020s
sys 0m0.028s
Мы также использовали time
измерить время запуска. Обратите внимание, что
Rscript
примерно в 5,5 раз быстрее, чемR
, Одна из причин в том, чтоR
загрузит 6 пакетов по умолчанию при запуске, в то время какRscript
загружает только одинbase
упаковка по контролю:--default-packages=base
, Но это все еще намного быстрее, даже без этой настройки.- В конце оба процесса запуска направлены на
$(R RHOME)/bin/exec/R
и в своем оригинальном посте я уже эксплуатировалreadelf -d
чтобы показать, что этот исполняемый файл будет загруженlibR.so
, которые связаны сlibblas.so.3
, Согласно объяснениям @Employed Russian, библиотека BLAS, загруженная первой, победит, поэтому мой оригинальный метод не будет работать. - Чтобы успешно запустить
strace
мы использовали удивительный файл/dev/null
как входной файл и выходной файл при необходимости. Например,Rscript
требует входной файл, в то время какR
требует обоих. Мы подаем нулевое устройство обоим, чтобы команда работала гладко и вывод был чистым. Нулевое устройство является физически существующим файлом, но работает потрясающе. При чтении из него ничего не содержится; во время записи он отбрасывает все.
2. Чит R
Сейчас с libblas.so
будет загружен в любом случае, единственное, что мы можем сделать, - это предоставить собственную версию этой библиотеки. Как я уже говорил в оригинальном сообщении, если у нас есть root-доступ, это действительно легко, используя update-alternatives --config libblas.so.3
, так что система Linux поможет нам завершить этот переход. Но @Employed Russian предлагает отличный способ обмануть систему без рут-доступа: давайте проверим, как R находит библиотеку BLAS при запуске, и убедимся, что мы передаем нашу версию до того, как будет найдена система по умолчанию! Чтобы отслеживать, как совместно используемые библиотеки находятся и загружаются, используйте переменную среды LD_DEBUG
,
Существует ряд переменных среды Linux с префиксом LD_
как указано в man ld.so
, Эти переменные могут быть назначены перед исполняемым файлом, чтобы мы могли изменить функцию запуска программы. Некоторые полезные переменные включают в себя:
LD_LIBRARY_PATH
для настройки пути поиска библиотеки времени выполнения;LD_DEBUG
для отслеживания поиска и загрузки общих библиотек;LD_TRACE_LOADED_OBJECTS
для отображения всей загруженной библиотеки программой (ведет себя подобноldd
);LD_PRELOAD
для принудительного внедрения библиотеки в программу с самого начала, до того, как будут найдены все остальные библиотеки;LD_PROFILE
а такжеLD_PROFILE_OUTPUT
для профилирования одной указанной общей библиотеки. Пользователь R, который прочитал раздел 3.4.1.1 sprof of Написание расширений R, должен помнить, что он используется для профилирования скомпилированного кода из R.
Использование LD_DEBUG
можно увидеть по:
~/Desktop/dgemm$ LD_DEBUG=help cat
Valid options for the LD_DEBUG environment variable are:
libs display library search paths
reloc display relocation processing
files display progress for input file
symbols display symbol table processing
bindings display information about symbol binding
versions display version dependencies
scopes display scope information
all all previous options combined
statistics display relocation statistics
unused determined unused DSOs
help display this help message and exit
To direct the debugging output into a file instead of standard output a filename can be specified using the LD_DEBUG_OUTPUT environment variable.
Здесь мы особенно заинтересованы в использовании LD_DEBUG=libs
, Например,
~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
5974: find library=libblas.so.3 [0]; searching
5974: trying file=/usr/lib/R/lib/libblas.so.3
5974: trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
5974: trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
5974: trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
5974: trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
5974: trying file=/usr/lib/i386-linux-gnu/libblas.so.3
5974: trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
5974: trying file=/usr/lib/libblas.so.3
5974: calling init: /usr/lib/libblas.so.3
5974: calling fini: /usr/lib/libblas.so.3 [0]
показывает различные попытки, которые программа R пыталась найти и загрузить libblas.so.3
, Так что, если бы мы могли предоставить нашу собственную версию libblas.so.3
и убедитесь, что R сначала его найдет, а затем проблема решена.
Давайте сначала сделаем символическую ссылку libblas.so.3
в нашем рабочем пути к библиотеке OpenBLAS libopenblas.so
, затем разверните по умолчанию LD_LIBRARY_PATH
с нашим рабочим путем (и экспортировать его):
~/Desktop/dgemm$ ln -sf libopenblas.so libblas.so.3
~/Desktop/dgemm$ export LD_LIBRARY_PATH = $(pwd):$LD_LIBRARY_PATH ## put our working path at top
Теперь давайте снова проверим процесс загрузки библиотеки:
~/Desktop/dgemm$ LD_DEBUG=libs Rscript --default-packages=base --vanilla /dev/null |& grep blas
6063: find library=libblas.so.3 [0]; searching
6063: trying file=/usr/lib/R/lib/libblas.so.3
6063: trying file=/usr/lib/i386-linux-gnu/i686/sse2/libblas.so.3
6063: trying file=/usr/lib/i386-linux-gnu/i686/cmov/libblas.so.3
6063: trying file=/usr/lib/i386-linux-gnu/i686/libblas.so.3
6063: trying file=/usr/lib/i386-linux-gnu/sse2/libblas.so.3
6063: trying file=/usr/lib/i386-linux-gnu/libblas.so.3
6063: trying file=/usr/lib/jvm/java-7-openjdk-i386/jre/lib/i386/client/libblas.so.3
6063: trying file=/home/zheyuan/Desktop/dgemm/libblas.so.3
6063: calling init: /home/zheyuan/Desktop/dgemm/libblas.so.3
6063: calling fini: /home/zheyuan/Desktop/dgemm/libblas.so.3 [0]
Большой! Мы успешно обманули Р.
3. Эксперимент с OpenBLAS
~/Desktop/dgemm$ Rscript --default-packages=base --vanilla mmperf.R
GFLOPs = 8.77
Теперь все работает как положено!
4. Unset LD_LIBRARY_PATH
(быть в безопасности)
Это хорошая практика, чтобы сбросить LD_LIBRARY_PATH
после использования.
~/Desktop/dgemm$ unset LD_LIBRARY_PATH