2016-05-05 2 views
3

Может ли кто-нибудь сказать мне, почему я могу не успешно проверить производительность OpenBLAS dgemm (в GFLOP) в R следующим образом?Без доступа к корню, запустите R с настроенным BLAS, когда он связан со ссылкой BLAS

  1. ссылка R с «эталонным BLAS» libblas.so
  2. компилироваться C программе mmperf.c с библиотекой OpenBLAS libopenblas.so
  3. нагрузки в результате общей библиотеки mmperf.so в R, вызовите функцию R обертки mmperf и сообщить dgemm производительности в GFlops.

Пункт 1 выглядит странно, но у меня нет выбора, потому что у меня нет доступа к корню на машинах, которые я хочу проверить, поэтому фактическое соединение с OpenBLAS невозможно. К «не успешно» Я имею в виду, что моя программа заканчивается сообщением dgemm для ссылки BLAS вместо OpenBLAS. Я надеюсь, что кто-то может мне объяснить:

  1. Почему мой способ не работает;
  2. это вообще возможно, чтобы заставить его работать (это важно, потому что если это невозможно, я должен написать функцию C main и делать свою работу в программе C.)

Я исследовал в этот вопрос в течение двух дней, здесь я буду включать различные системные выходы, чтобы помочь вам сделать диагноз. Чтобы сделать вещи воспроизводимыми, я также буду включать код, makefile, а также команду оболочки.

Часть 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, так как это игнорирован, который во время выполнения загрузчик будет искать:

~/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: код С, Makefile, и R код

Здесь A C сценарий mmperf.c вычислительные GFlops умножения 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); 
    } 

Вот простой Р сценарий 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")) 

Наконец, есть простой Makefile для создания разделяемой библиотеки 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 в какой-то момент по какой-то причине? Любые объяснения и решения? Благодаря!

+0

Пробовали ли вы иметь '.' первым в своем LD_LIBRARY_PATH? –

+0

Благодарим вас за то, что вы хотите суммировать материал на этой странице, Zheyuan - однако, пожалуйста, не делайте этого! Золотое правило с сайтами Q & A заключается в том, чтобы сохранить этот вопрос в значительной степени, как вы видели ситуацию, прежде чем обнаружили ответ. Вопрос, содержащий преамбулу, относящуюся к частям ответа, уже не является вопросом, и он отклоняется от формата Q & A, который мы предпочитаем. Вы можете добавить свое резюме в свой ответ, но я не думаю, что это необходимо. (Мы также не редактируем [решенные] сообщения в заголовки - сохраняйте их оригинальными тоже!). – halfer

ответ

4

почему мой способ не работает

Во-первых, совместно используемые библиотеки в UNIX предназначены для имитации пути архивные библиотеки работы (архив библиотеки были там первым).В частности, это означает, что если у вас есть libfoo.so и libbar.so, оба определяющих символа foo, то какая бы ни была загружена библиотека, первая побеждает: все ссылки на foo из любой точки программы (в том числе от libbar.so) свяжутся с определением libfoo.so s foo.

Это подражает, что произойдет, если вы связали свою программу с libfoo.a и libbar.a, где обе библиотеки архивов определили один и тот же символ foo. Дополнительная информация по ссылке архива here.

Это должно быть ясно из выше, что если 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 является крошечного бинарного (11K в моей системе, по сравнению с 2.4Mb для 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 не быть на самом деле используется для всего.

это вообще возможно, чтобы заставить его работать

Возможно. Я могу думать о двух возможных решений:

  1. Притворись, что на самом деле libopenblas.so.0libblas.so.3
  2. Перестроить всю 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, вы сказали:

У меня нет доступа корня на машинах, которые я хочу, чтобы проверить, поэтому фактическая связь с OpenBLAS невозможна.

, но это, кажется, фиктивный аргумент: если вы можете построить libopenblas, конечно, вы также можете создать свою собственную версию R.

Update:

Вы упомянули в начале, что 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.

Однако, это делает library interpositioning трудным и работает совершенно по-другому от того, как работают архивные библиотеки (даже в Windows).

Мнения отличаются от того, какой дизайн лучше всего в целом, но ни одна система, вероятно, никогда не изменит свою модель.

Есть способы для UNIX подражать привязке символов в стиле Windows: см. RTLD_DEEPBIND в dlopen man page. Остерегайтесь: они чреваты опасностью, скорее всего, путают экспертов UNIX, широко не используются и, вероятно, будут иметь ошибки в реализации.

Update 2:

вы имеете в виду компилировать R и установить его под домашний каталог?

Да.

Затем, когда я хочу вызвать его, я должен явно указать путь к моей версии исполняемой программы, иначе может быть вызвана одна из них в системе? Или, могу ли я поместить этот путь в первую позицию переменной окружения $ PATH, чтобы обмануть систему?

В любом случае работает.

+0

@ZheyuanLi Я обновил ответ. –

+0

'UND' не определено, то есть импортировано из какой-либо другой библиотеки. –

+0

@ZheyuanLi Предлагаю вам задать другой вопрос, показать точные ошибки, которые вы получаете от установки LD_PRELOAD и т. Д. –

2

*********************

Решение 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 для измерения времени запуска. Отметьте, что

  1. Rscript примерно в 5,5 раз быстрее, чем R. Одна из причин заключается в том, что R загрузит 6 пакетов по умолчанию при запуске, а Rscript загружает только один пакет base: --default-packages=base. Но это еще намного быстрее, даже без этой настройки.
  2. В конце оба процесса запуска направлены на $(R RHOME)/bin/exec/R, и в моем исходном сообщении я уже использовал readelf -d, чтобы показать, что этот исполняемый файл будет загружен libR.so, которые связаны с libblas.so.3. Согласно объяснению @Employed Russian, первая загруженная библиотека BLAS победит, поэтому мой оригинальный метод не будет работать.
  3. Чтобы успешно запустить strace, мы использовали файл/dev/null в качестве входного файла и выходного файла при необходимости. Например, Rscript требует входной файл, а R требует и то, и другое.Мы подаем нулевое устройство на оба, чтобы команда выполнялась плавно, а выход был чистым. Нулевое устройство - физически существующий файл, но работает удивительно. Читая от него, он ничего не содержит; при написании к ней он отбрасывает все.

2. Чит R

Теперь, так как libblas.so будет загружен в любом случае, единственное, что мы можем сделать, это обеспечить собственную версию этой библиотеки. Как я уже сказал в исходном посте, если у нас есть root-доступ, это очень просто, используя update-alternatives --config libblas.so.3, так что система Linux поможет нам завершить этот переключатель. Но @Employed Russian предлагает потрясающий способ обмануть систему без корневого доступа: давайте проверим, как R находит библиотеку BLAS при запуске, и убедитесь, что мы загружаем нашу версию до того, как найдена система по умолчанию! Чтобы отслеживать обнаружение и загрузку разделяемых библиотек, используйте переменную окружения LD_DEBUG.

Есть номер Linux environment variables с префиксом 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 из Writing R extensions следует напомнить, что это используется для профилирования скомпилированного кода из внутри 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] 

Отлично! Мы успешно обманули R.

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 
1

*********************

Решение 2:

*********** **********

Здесь мы предлагаем другое решение, используя переменную окружения LD_PRELOAD, упомянутую в нашем solution 1. Использование LD_PRELOAD более «жестокое», поскольку оно заставляет загружать данную библиотеку в программу перед любой другой программой, даже до библиотеки C libc.so! Это часто используется для срочного исправления в разработке Linux.

Как показано в части 2 original post, общая BLAS библиотека libopenblas.so имеет libopenblas.so.0 игнорирован. игнорирована внутреннее имя, динамическая библиотека загрузчик будет искать во время выполнения, так что нам нужно сделать символическую ссылку на libopenblas.so с этим игнорирована:

~/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] 

Сравнивая с тем, что мы видели в solution 1 обманывая R с нашей собственной версией libblas.so.3, мы можем видеть, что

  • libopenblas.so.0 сначала загружается, следовательно, сначала определяется Rscript;
  • после libopenblas.so.0 не найдено, Rscript продолжает поиск и загрузку libblas.so.3. Однако это не повлияет на правило «первым пришел, первым подаст», объясненный в original answer.

Хорошо, все работает, поэтому мы тестируем нашу mmperf.c программу:

~/Desktop/dgemm$ Rscript --default-packages=base --vanilla mmperf.R 
GFLOPs = 9.62 

Результат 9,62 больше, чем 8,77 мы видели в предыдущем решении просто случайно. В качестве теста на использование OpenBLAS мы не запускаем эксперимент много раз для результата preciser.

Тогда, как обычно, снята с переменной окружения в конце:

~/Desktop/dgemm$ unset LD_PRELOAD 
Смежные вопросы