Lapack: Izinkan kemungkinan pemasangan pustaka indeks-64 bersama pustaka indeks-32 standar?

Dibuat pada 1 Nov 2020  ·  41Komentar  ·  Sumber: Reference-LAPACK/lapack

Saat ini debian memiliki (beberapa) header terpisah untuk blas64 dan cblas64 (meskipun bukan dari implementasi referensi).

Saya tidak yakin apakah itu benar atau tidak, sehubungan dengan referensi index64 API (mereka berasal dari perpustakaan blis).

Apakah mungkin untuk menambahkan opsi ke cmake, sesuatu seperti BUILD_INDEX64 , yang didefaultkan ke OFF , tetapi jika telah diaktifkan, itu akan membuat perpustakaan indeks-64?
Jika saya membuat PR untuk opsi seperti itu, apakah itu akan dianggap sebagai kemungkinan?

Beberapa hal yang saya pikirkan untuk memungkinkan ini hidup berdampingan dengan instalasi standar - beri nama perpustakaan menjadi libblas64.so, libcblas64.so, liblapack64.so, liblapacke64.so , dengan cara ini tidak ada konflik antara nama perpustakaan (meskipun tentu saja, Anda tidak dapat menautkan dengan libblas dan libblas64 secara bersamaan).
Juga perpustakaan perlu dikompilasi dua kali, sekali untuk index32 dan sekali untuk index64 (tapi itu adalah skenario yang sangat normal dan bukan pemecah kesepakatan).
Satu-satunya konflik yang saya tidak dapat memikirkan resolusi yang baik adalah nama file header.
Jika mengikuti gaya debian, mungkin bijaksana untuk memanggil header c yang diakhiri dengan 64 juga.
(Saya memelihara ini untuk Gentoo dan ingin menjaga ekosistem sangat dekat dengan debian, sehingga kami memiliki sedikit masalah bagi pengembang yang berpindah antar sistem)

Saya terbuka untuk saran apapun sebelum saya membuat PR :heart:

Terima kasih,
Aisyah

Build System Enhancement

Semua 41 komentar

Hai Aisha, itu sangat masuk akal bagi saya, tetapi mari kita lihat apakah kami memiliki umpan balik dari orang lain. Jadi mari kita tunggu beberapa hari. J

Yang pasti, itu terdengar seperti rencana yang bagus.

OMG @langou
kamu cepat sekali :hati:

Sekadar kelengkapan, saya menuliskan hal-hal yang masih harus kita lakukan:

  • Cari tahu cara memberi nama header sehingga API 32bit dapat berdampingan dengan API 64bit
  • Perbaiki pernyataan printf/fprintf sehingga pernyataan tersebut menggunakan qualifier yang benar untuk pencetakan.

Setiap saran untuk menyelesaikan poin pertama dipersilakan, sayangnya saya tidak memiliki solusi "bersih".

Beberapa pertanyaan, yang akan membantu saya menangani penamaan file

  • Tampaknya ada banyak definisi duplikat antara cblas_f77.h dan cblas_test.h . Apakah kita benar-benar membutuhkan itu?
  • Apakah cblas_test.h perlu diinstal? Mengingat namanya (dan file yang digunakannya) saya menganggapnya hanya digunakan selama fase pengujian. Mungkin kita tidak harus menginstal file ini pada tingkat seluruh sistem?

Hai @epsilon-0,

Beberapa hal yang saya pikirkan untuk memungkinkan ini hidup berdampingan dengan instalasi standar - beri nama perpustakaan menjadi libblas64.so, libcblas64.so, liblapack64.so, liblapacke64.so, dengan cara ini tidak ada konflik antara nama perpustakaan ( meskipun tentu saja, Anda tidak dapat menautkan dengan libblas dan libblas64 secara bersamaan).

Anda mungkin mencari PR #218. Penulis PR ini adalah Björn Esser dari Proyek Fedora.

Hai @epsilon-0. Apakah #462 menyelesaikan masalah ini?

@weslleyspereira tidak, ini belum selesai.
Perlu ada beberapa penggantian nama/penanganan tajuk yang dilakukan.
Saya sibuk selama beberapa minggu ke depan jadi saya tidak akan bisa melakukan ini segera.
Garis besar dasar

  • file header harus disebut cblas.h dan cblas64.h , demikian juga untuk header lainnya

    • ini berarti file *.c memerlukan sedikit penyesuaian untuk menyertakan header yang tepat, tetapi ini hanya selama waktu pembuatan, sehingga dapat diretas.

  • file cmake harus diinstal di bawah lapack64 atau cblas64 , dll.

Ok aku paham. Terima kasih atas tindak lanjut yang cepat!

Saya memiliki masalah yang sama mencoba mengemas sesuatu dengan pkgsrc. Saya ingin menginstal referensi secara lengkap, dengan cblas dan lapacke. Untuk implementasi berbeda yang diinstal pada saat yang sama, saya memilih nama perpustakaan dan sub-direktori yang berbeda untuk header, jadi misalnya

/usr/lib/libopenblas.so
/usr/lib/libopenblas64.so
/usr/lib/libblas.so
/usr/lib/libcblas.so
/usr/lib/libblas64.so
/usr/lib/libcblas64.so
/usr/include/openblas/cblas.h
/usr/include/openblas64/cblas.h
/usr/include/netlib/cblas.h
/usr/include/netlib64/cblas.h
/usr/include/cblas.h -> netlib/cblas.h (for compatibility, having the default)

(Dan seterusnya)

Kami tidak mempertimbangkan peralihan runtime seperti distro biner, jadi boleh saja jika setiap cblas.h (dan lapacke.h) spesifik untuk pustaka yang cocok, seperti dengan nama tambahan untuk libopenblas. Pemilihan waktu pembuatan terjadi melalui

BLAS_INCLUDES=-I/prefix/include/netlib64
BLAS_LIBS=-lblas64
CBLAS_LIBS=-lcblas64

(dll.) Itulah yang seharusnya dikatakan oleh file .pc, dan itu jauh lebih mudah daripada mengomunikasikan nama file header yang berbeda. Mereka belum konsisten dalam hal itu, tetapi saya sedang memperbaikinya. Tampaknya sejauh ini orang baru saja meretasnya di distro mereka, jika mengganggu semua lib referensi sama sekali.

Saya punya satu pertanyaan tentang header itu.

Saya meretas cmake build untuk membuat setiap komponen dibuat secara terpisah, dan saya mencoba beberapa perbaikan lainnya (lihat https://github.com/Reference-LAPACK/lapack/pull/556). Saya mendapatkan perpustakaan libblas.so dan libblas64.so yang dibangun dengan baik, saya mendapatkan direktori header yang dikonfigurasi ... tetapi cblas.h dan lapacke.h yang diinstal identik untuk versi pengindeksan 32 dan 64 bit. Ini bertentangan dengan openblas: Di sana, saya mendapat perbedaan penting yang tidak saya lihat untuk build netlib:

diff -ruN /data/pkg/include/openblas/openblas_config.h /data/pkg/include/openblas64/openblas_config.h
--- /data/pkg/include/openblas/openblas_config.h    2021-06-03 19:03:53.000000000 +0200
+++ /data/pkg/include/openblas64/openblas_config.h  2021-06-03 19:13:36.000000000 +0200
@@ -44,6 +44,7 @@
 #define OPENBLAS_DLOCAL_BUFFER_SIZE 32768
 #define OPENBLAS_CLOCAL_BUFFER_SIZE 16384
 #define OPENBLAS_ZLOCAL_BUFFER_SIZE 12288
+#define OPENBLAS_USE64BITINT 
 #define OPENBLAS_GEMM_MULTITHREAD_THRESHOLD 4
 #define OPENBLAS_VERSION " OpenBLAS 0.3.15 "
 /*This is only for "make install" target.*/

Untuk pustaka referensi, semua header dari build indeks 32 dan 64 bit adalah identik dan tampaknya pengguna diharapkan untuk menempatkan
-DWeirdNEC di flag mereka (mungkin lucu 30 tahun yang lalu) untuk cblas.h dan -DLAPACK_ILP64 -DHAVE_LAPACK_CONFIG_H . Karena orang menggunakan perpustakaan BLAS yang dioptimalkan dalam produksi, standar de facto tidak untuk mengeksposnya kepada pengguna. Umpan balik ini pada referensi, IMHO, dan header yang diinstal dari build ILP64 seharusnya tidak memerlukan flag yang funky untuk menghindari crash aplikasi Anda saat menautkan ke lib 64 bit.

Apakah kami setuju bahwa itu adalah solusi yang tepat untuk memodifikasi header pada waktu pembuatan untuk menentukan bilangan bulat yang benar?

Btw, file konfigurasi cblas yang diinstal juga kehilangan referensi apa pun ke def yang diperlukan, jadi rusak untuk build indeks 64 bit, seperti yang terlihat. Tapi sebenarnya, saya berpikir untuk tidak menginstal ini sama sekali. Mereka berlebihan dengan file .pc dan membuatnya mungkin lebih sulit untuk meyakinkan paket dependen yang menggunakan cmake untuk menerima pilihan pembuat paket melalui BLAS_LIBS dkk.

PS: Dengan Intel MKL, ada sakelar pusat -DMKL_ILP64 harus disetel. Saya membayangkan pengaturan sepele
include/intel-mkl64/cblas.h dengan

#ifndef MKL_ILP64
#define MKL_ILP64
#endif
#include <mkl_cblas.h>

agar sesuai dengan skema umum. Saya juga bisa memasukkan definisi ke dalam BLAS_INCLUDES, sama untuk definisi netlib yang aneh. Apa yang lebih baik? Apakah kita ingin melakukannya seperti Intel atau seperti OpenBLAS?

Apakah kami setuju bahwa itu adalah solusi yang tepat untuk memodifikasi header pada waktu pembuatan untuk menentukan bilangan bulat yang benar?

Ya. Saya setuju dengan itu, dan lebih suka solusi yang tidak mereplikasi seluruh header. Menurut saya lebih bersih.

Btw, file konfigurasi cblas yang diinstal juga kehilangan referensi apa pun ke def yang diperlukan, jadi rusak untuk build indeks 64 bit, seperti yang terlihat.

Benar. Saya baru saja menginstal perpustakaan 64-bit (BUILD_INDEX64=ON) dan tidak dapat melihat apa pun yang menyuruh saya untuk menggunakan WeirdNEC , LAPACK_ILP64 atau HAVE_LAPACK_CONFIG_H . Terima kasih telah memperhatikan itu!

Ya. Saya setuju dengan itu, dan lebih suka solusi yang tidak mereplikasi seluruh header. Menurut saya lebih bersih.

Ini ambigu bagi saya. Manakah solusi yang lebih bersih? Yang saya persiapkan sekarang adalah:

#if defined(WeirdNEC) || @HAVE_ILP64@
   #define CBLAS_INDEX long
   #ifndef WeirdNEC
   #define WeirdNEC
   #endif
#else
   #define CBLAS_INDEX int
#endif

CMakeFile akan menggantikan HAVE_ILP dengan 1 atau 0, header yang dihasilkan sedang diinstal untuk build saat ini.

(Btw.: long tidak akan berfungsi di Windows. Lama di sana ... atau int64_t di semua platform dengan stdint.)

Benar. Saya baru saja menginstal perpustakaan 64-bit (BUILD_INDEX64=ON) dan tidak dapat melihat apa pun yang menyuruh saya untuk menggunakan WeirdNEC , LAPACK_ILP64 atau HAVE_LAPACK_CONFIG_H . Terima kasih telah memperhatikan itu!

Saya membayangkan masa depan di mana Anda melakukannya

cc -I/foo/include/netlib64 -o bar bar.c -L/foo/lib -lcblas64

Dan hal-hal ditangani di foo/include/netlib64/cblas.h, jika tidak oleh foo/include/netlib/cblas.h (mungkin ditautkan ke foo/include/cblas.h).

Saya memiliki kecurigaan bahwa ini _bukan_ yang Anda maksud, tetapi saya ingin meyakinkan bahwa itu lebih baik;-)

Anda dapat mencoba untuk tidak menduplikasi header dengan menempatkan header 'the' di /foo/include/cblas.h dan meminta /foo/include/netlib64/cblas.h menyertakan header itu hanya dengan mendefinisikan WeirdNEC, tetapi itu berarti bahwa 64 paket bit dan 32 bit berbagi file header umum itu, yang berantakan untuk pengemasan. Jauh lebih baik jika masing-masing meletakkan filenya ke tempat/nama yang terpisah. Nama harus tetap cblas.h karena Anda tidak ingin mengganti baris #include <cblas.h> .

Sunting: Juga, memiliki cblas.h termasuk ../cblas.h berantakan dengan sendirinya. Kami juga mendefinisikan direktori instalasi _one_ header untuk cmake. Secara default itu /foo/include, bukan /foo/netlib64/include. Saya tidak akan mengubah default ini. Pembuat paket harus menentukan subdirektori seperti ini (BSD make in pkgsrc):

.if !empty(LAPACK_COMPONENT:M*64)
.  if empty(MACHINE_ARCH:M*64)
PKG_FAIL_REASON+=       "${LAPACK_COMPONENT} incompatible with non-64-bit platform"
.  endif
HEADERDIR=netlib64
.else
HEADERDIR=netlib
.endif

# Note: We patch the build to install both static and
# shared libraries.
CMAKE_ARGS=     -DBUILD_DEPRECATED=ON \
                -DBUILD_SHARED_LIBS=ON \
                -DBUILD_STATIC_LIBS=ON \
                -DCMAKE_INSTALL_INCLUDEDIR=${PREFIX}/include/${HEADERDIR} \
                ${LAPACK_COMPONENT_CMAKE_ARGS}

Aspek yang indah dari pengiriman/pemasangan cblas.h 32 bit dengan modifikasi ini ke lokasi biasa adalah bahwa mekanik asli masih bekerja. Hanya varian 64 bit yang akan menerapkan WeirdNEC. Anda dapat memutuskan untuk hanya menginstal 64 bit menjadi awalan dan menjaga bagian lain dari ekosistem tidak tersentuh.

Oh ayolah… CBLAS/cmake/cblas-config-install.cmake.in sepertinya lupa -DCMAKE_INSTALL_INCLUDEDIR ya?

# Report lapacke header search locations.
set(CBLAS_INCLUDE_DIRS ${_CBLAS_PREFIX}/include)

(Komentarnya adalah gula di atas.)

Saya merasa bahwa build CMake jauh lebih tidak matang dari yang dipikirkan orang. Apakah proyek ini serius untuk menjadikannya sebagai bangunan utama atau ini hanya kontribusi drive-by? Saya benar-benar tergoda untuk memperbaiki Makefile gaya lama, lebih sedikit keributan. Tapi sekarang saya menghabiskan begitu banyak waktu untuk memperbaiki hal-hal CMake, yang tetap saya benci. Jadi saya ingin menyelesaikannya.

Saya harus menyerah sekarang ... Saya berhasil memindahkan cblas.h ke cblas.h.in seperti yang ditunjukkan di atas, dan menambahkan

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cblas.h.in cblas.h @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cblas_f77.h.in cblas_f77.h @ONLY)

ke CBLAS/include/CMakeLists.txt, juga telah mendefinisikan @HAVE_ILP64@ menjadi 1 atau 0 di CMakeLists.txt tingkat atas. Tapi saya bisa seumur hidup saya tidak menemukan cara untuk membuat hal-hal instal yang ada di CMakeLists.txt tingkat yang lebih tinggi menginstal header yang dihasilkan, atau salinan aneh yang sama dari ${LAPACK_BINARY_DIR}/include (benarkah? A salin di dalam pohon sumber?)

Apa yang seharusnya dilakukan makro append_subdir_files? Tampaknya menambahkan salinan awalan ke jalur header. Saya tidak mendapatkan cukup atau terlalu banyak jalur ke file header sumber. Saya hanya ingin menginstal file header dari SINI ke SANA, sial.

Dapatkah seseorang yang berpengetahuan membantu di sini? Saya kira saya bisa mengetahuinya besok, tetapi saya tidak yakin apakah itu tanpa menghancurkan sesuatu di dunia nyata untuk kelegaan emosional.

Ya. Saya setuju dengan itu, dan lebih suka solusi yang tidak mereplikasi seluruh header. Menurut saya lebih bersih.

Ini ambigu bagi saya. Manakah solusi yang lebih bersih? Yang saya persiapkan sekarang adalah:

#if defined(WeirdNEC) || @HAVE_ILP64@
   #define CBLAS_INDEX long
   #ifndef WeirdNEC
   #define WeirdNEC
   #endif
#else
   #define CBLAS_INDEX int
#endif

CMakeFile akan menggantikan HAVE_ILP dengan 1 atau 0, header yang dihasilkan sedang diinstal untuk build saat ini.

(Btw.: long tidak akan berfungsi di Windows. Lama di sana ... atau int64_t di semua platform dengan stdint.)

Benar. Saya baru saja menginstal perpustakaan 64-bit (BUILD_INDEX64=ON) dan tidak dapat melihat apa pun yang menyuruh saya untuk menggunakan WeirdNEC , LAPACK_ILP64 atau HAVE_LAPACK_CONFIG_H . Terima kasih telah memperhatikan itu!

Saya membayangkan masa depan di mana Anda melakukannya

cc -I/foo/include/netlib64 -o bar bar.c -L/foo/lib -lcblas64

Dan hal-hal ditangani di foo/include/netlib64/cblas.h, jika tidak oleh foo/include/netlib/cblas.h (mungkin ditautkan ke foo/include/cblas.h).

Saya memiliki kecurigaan bahwa ini _bukan_ yang Anda maksud, tetapi saya ingin meyakinkan bahwa itu lebih baik;-)

Maaf, izinkan saya menjelaskan. Pada awalnya, saya menyukai ide untuk mempertahankan header asli cblas.h dan membuat include/netlib64/cblas.h dan include/netlib/cblas.h dengan sesuatu seperti

#if defined(WeirdNEC)
   #define WeirdNEC
#endif
#include <cblas.h>

Anda dapat mencoba untuk tidak menduplikasi header dengan menempatkan header 'the' di /foo/include/cblas.h dan meminta /foo/include/netlib64/cblas.h menyertakan header itu hanya dengan mendefinisikan WeirdNEC, tetapi itu berarti bahwa 64 paket bit dan 32 bit berbagi file header umum itu, yang berantakan untuk pengemasan. Jauh lebih baik jika masing-masing meletakkan filenya ke tempat/nama yang terpisah. Nama harus tetap cblas.h karena Anda tidak ingin mengganti baris #include <cblas.h> .

Sunting: Juga, memiliki cblas.h termasuk ../cblas.h berantakan dengan sendirinya. Kami juga mendefinisikan direktori instalasi _one_ header untuk cmake.

tapi ya, kita harus menggunakan include/netlib64 dan include sebagai direktori include jika satu header menyertakan yang lain.

Secara default itu /foo/include, bukan /foo/netlib64/include. Saya tidak akan mengubah default ini. Pembuat paket harus menentukan subdirektori seperti ini (BSD make in pkgsrc):

.if !empty(LAPACK_COMPONENT:M*64)
.  if empty(MACHINE_ARCH:M*64)
PKG_FAIL_REASON+=       "${LAPACK_COMPONENT} incompatible with non-64-bit platform"
.  endif
HEADERDIR=netlib64
.else
HEADERDIR=netlib
.endif

# Note: We patch the build to install both static and
# shared libraries.
CMAKE_ARGS=     -DBUILD_DEPRECATED=ON \
                -DBUILD_SHARED_LIBS=ON \
                -DBUILD_STATIC_LIBS=ON \
                -DCMAKE_INSTALL_INCLUDEDIR=${PREFIX}/include/${HEADERDIR} \
                ${LAPACK_COMPONENT_CMAKE_ARGS}

Itu menurut saya bagus. Jadi, Anda hanya akan menambahkan alternatif untuk membangun LAPACK tanpa harus _menebak_ flag compiler. Tetapi cara saat ini juga akan berhasil.

(Btw.: long tidak akan berfungsi di Windows. Lama di sana ... atau int64_t di semua platform dengan stdint.)

Senang mendengarnya. BLAS++ dan LAPACK++ menggunakan int64_t alih-alih long long.

@weslleyspereira Jadi Anda pada awalnya menyukai ide ini:

#if defined(WeirdNEC)
   #define WeirdNEC
#endif
#include "../cblas.h"

dengan /prefix/include/cblas.h dan /prefix/include/netlib64/cblas.h, yang terakhir menemukan yang pertama? Tetapi Anda sekarang setuju bahwa ini adalah solusi yang lebih kuat untuk menginstal header yang terlihat seperti ini untuk build 64 bit?

#if defined(WeirdNEC) || @HAVE_ILP64@
   #define CBLAS_INDEX long
   #ifndef WeirdNEC
   #define WeirdNEC
   #endif
#else
   #define CBLAS_INDEX int
#endif

(long vs. int64 adalah masalah yang berbeda, tetapi saya siap untuk melakukan perubahan itu, sama seperti BLAS++)

Heck, saya bahkan tidak yakin apakah aman untuk berasumsi bahwa `#include ".../cblas.h" hanya akan menemukan header indentasi lainnya. Standar C tampaknya mengatakan bahwa urutan pencarian ditentukan oleh implementasi, belum tentu relatif terhadap header saat ini. Masalah utama saya sebagai pembuat paket adalah bahwa saya memerlukan paket terpisah untuk header umum itu atau memiliki paket 64 bit bergantung pada paket 32 ​​bit hanya untuk itu. Ini akan menyebalkan.

Saya benar-benar ingin maju sekarang dengan perubahan untuk pkgsrc, untuk menyelesaikan perubahan untuk kode upstream nanti. Kita bisa mendiskusikan simbol baru untuk memaksa indeks 32 bit atau indeks 64 bit secara eksplisit dengan salah satu header ( -DNETLIB_INDEX_BITS=64 ?), hanya default untuk apa perpustakaan dibangun.

Bisakah saya mendapatkan persetujuan tentang solusi yang kami maksudkan menjadi ini?

lib/libcblas64.so
include/optional_subdir64/cblas.h

dan

lib/libcblas.so
include/optional_subdir/cblas.h

Setiap build kode LAPACK menghasilkan header yang, setidaknya secara default, cocok dengan library yang diinstal tanpa pengguna mendefinisikan apa pun. OKE?

Saya kemudian dapat memasukkan ini sebelum rilis pkgsrc yang akan datang (batas waktu semakin dekat) dan kita dapat mendiskusikan lebih lanjut detail implementasi itu sehingga saya dapat menghapus tambalan setelah menggabungkan sesuatu di sini, dengan rilis LAPACK baru. Dengan perubahan ini, build Makefile biasa juga perlu diperbaiki, tetapi saya belum membutuhkannya untuk patch _my_ ketika saya hanya menggunakan build CMake.

(Hanya perlu entah bagaimana memeriksa emosi saya ketika mencoba mengalahkan build CMake yang aneh itu ke dalam penyerahan, di mana ia mengacak salinan header di sekitar direktori build dan kemudian tidak dapat menemukannya untuk diinstal. Atau putuskan tentang file .cmake yang rusak yang berguna bagi kita, mungkin lepaskan saja dari instal ... kami mendapat pkg-config!)

Apa pun? Saya harus mengakui bahwa saya tidak melihat banyak peluang untuk solusi yang berbeda dalam praktiknya, karena ini adalah contoh yang ditetapkan oleh openblas, implementasi utama yang kami gunakan. Saya bisa membayangkan meyakinkan Intel untuk memiliki subdirektori untuk header indeks 64 bit/32 bit, juga, membungkus mkl_cblas.h dan mkl_lapacke.h mereka. Kalau tidak, saya membuat paket sederhana yang hanya menyediakan itu.

include/mkl-blas/cblas.h
include/mkl-blas64/cblas.h

Saat ini, saya menambahkan mesin ke pkgsrc untuk menyediakan build dengan baris -DWeirdNEC -DHAVE_LAPACK_CONFIG_H -DLAPACK_ILP64 lucu, dengan cblas dan cblas64 memasang header yang identik. Itu bisa tetap seperti itu, tetapi saya masih berpikir masuk akal untuk mengatur header agar sesuai dengan build ABI.

@weslleyspereira Jadi Anda pada awalnya menyukai ide ini:

#if defined(WeirdNEC)
   #define WeirdNEC
#endif
#include "../cblas.h"

dengan /prefix/include/cblas.h dan /prefix/include/netlib64/cblas.h, yang terakhir menemukan yang pertama? Tetapi Anda sekarang setuju bahwa ini adalah solusi yang lebih kuat untuk menginstal header yang terlihat seperti ini untuk build 64 bit?

#if defined(WeirdNEC) || @HAVE_ILP64@
   #define CBLAS_INDEX long
   #ifndef WeirdNEC
   #define WeirdNEC
   #endif
#else
   #define CBLAS_INDEX int
#endif

Ya, itu saja. Saya setuju dengan solusi Anda memiliki subfolder untuk header 32- dan 64-bit. Saya mendiskusikan ini dengan @langou , dan dia juga yakin ini akan menjadi solusi yang baik.

(long vs. int64 adalah masalah yang berbeda, tetapi saya siap untuk melakukan perubahan itu, sama seperti BLAS++)

Benar. Ini harus dibahas dalam masalah lain.

Saya benar-benar ingin maju sekarang dengan perubahan untuk pkgsrc, untuk menyelesaikan perubahan untuk kode upstream nanti. Kita bisa mendiskusikan simbol baru untuk memaksa indeks 32 bit atau indeks 64 bit secara eksplisit dengan salah satu header ( -DNETLIB_INDEX_BITS=64 ?), hanya default untuk apa perpustakaan dibangun.

Bisakah saya mendapatkan persetujuan tentang solusi yang kami maksudkan menjadi ini?

lib/libcblas64.so
include/optional_subdir64/cblas.h

dan

lib/libcblas.so
include/optional_subdir/cblas.h

Ya. Saya pikir Anda bisa maju dan mengusulkan PR di masa depan, terima kasih! Saya pribadi berpikir simbol baru seperti NETLIB_INDEX_BITS masuk akal. Saya hanya akan memastikan nilai default tetap 32, dan itu -DWeirdNEC menyiratkan -DNETLIB_INDEX_BITS=64 .

Setiap build kode LAPACK menghasilkan header yang, setidaknya secara default, cocok dengan library yang diinstal tanpa pengguna mendefinisikan apa pun. OKE?

Terdengar bagus untukku.

Saya kemudian dapat memasukkan ini sebelum rilis pkgsrc yang akan datang (batas waktu semakin dekat) dan kita dapat mendiskusikan lebih lanjut detail implementasi itu sehingga saya dapat menghapus tambalan setelah menggabungkan sesuatu di sini, dengan rilis LAPACK baru. Dengan perubahan ini, build Makefile biasa juga perlu diperbaiki, tetapi saya belum membutuhkannya untuk patch _my_ ketika saya hanya menggunakan build CMake.

Oke! Kami mungkin akan memiliki rilis LAPACK pada semester kedua tahun 2021. Dan ya, Makefile harus disesuaikan, dan saya bersedia membantu untuk itu.

Ini agak terkait. Kita tidak boleh lupa bahwa header untuk netlib CBLAS tidak hanya disediakan oleh netlib … NumPy selalu menggunakan headernya sendiri:

https://github.com/numpy/numpy/blob/main/numpy/core/src/common/npy_cblas.h

Dan di header ini, ia menetapkan CBLAS_INDEX=size_t , berbeda dari tipe integer yang digunakan dalam menentukan indeks. Ini digunakan hanya untuk mengembalikan nilai beberapa fungsi:

$ grep CBLAS_INDEX ./numpy/core/src/common/npy_cblas_base.h                                                                                                                                  
CBLAS_INDEX BLASNAME(cblas_isamax)(const BLASINT N, const float  *X, const BLASINT incX);
CBLAS_INDEX BLASNAME(cblas_idamax)(const BLASINT N, const double *X, const BLASINT incX);
CBLAS_INDEX BLASNAME(cblas_icamax)(const BLASINT N, const void   *X, const BLASINT incX);
CBLAS_INDEX BLASNAME(cblas_izamax)(const BLASINT N, const void   *X, const BLASINT incX);

Perbedaan:

$ grep cblas_isamax ./numpy/core/src/common/npy_cblas_base.h  /data/pkg/include/cblas.h                                                                                                      
./numpy/core/src/common/npy_cblas_base.h:CBLAS_INDEX BLASNAME(cblas_isamax)(const BLASINT N, const float  *X, const BLASINT incX);
/data/pkg/include/cblas.h:CBLAS_INDEX cblas_isamax(const CBLAS_INDEX N, const float  *X, const CBLAS_INDEX incX);

Aku ingin tahu apakah itu mungkin menyebabkan masalah. Untuk Netlib, hanya ada satu jenis indeks, sedangkan implementasi lainnya menggunakan jenis nilai balik yang berbeda untuk fungsi indeks. OpenBLAS memberikan contoh. Mereka mengatakan isamax mengembalikan unsigned size_t, tetapi pembungkus C sebenarnya memanggil fungsi Fortran yang mengembalikan integer yang ditandatangani (Sunting: Subrutin yang menulis nilai integer 32 atau 64 bit yang ditandatangani ke referensi yang diserahkan ke variabel 64 bit yang tidak ditandatangani pada 64 sistem bit).

Apakah implementasi referensi memiliki pendapat tentang ini? Saya _menebak_ tidak ada masalah nyata, karena nilai size_t akan selalu dapat menahan pengembalian non-negatif dari isamax(). Tapi baunya busuk. (Sunting: Anda bisa membangun dengan indeks 64 bit pada sistem 32 bit di mana size_t adalah 32 bit, kan? Kemudian Anda mendapat overflow. Selain ketidaknyamanan casting size_t * ke int * .)

Karena implementasi yang dioptimalkan tampaknya telah memutuskan size_t di sana, haruskah referensi menerima fakta itu dan mengikuti?

Dan seberapa berbahayakah sebenarnya, untuk menautkan numpy dengan referensi cblas?

OpenBLAS memberikan contoh. (...)
Karena implementasi yang dioptimalkan tampaknya telah memutuskan size_t di sana, haruskah referensi menerima fakta itu dan mengikuti?

Saya tentu saja tidak dapat berbicara untuk numpy (atau mkl dll. dalam hal ini), tetapi saya ragu untuk mengklaim OpenBLAS sebagai normatif dalam bentuk apa pun, paling tidak relatif terhadap apa yang (saya percaya) umumnya dilihat sebagai implementasi referensi __the__ .. .

Tentu. Hanya saja OpenBLAS atau MKL adalah apa yang digunakan orang dalam praktik dan keduanya tampaknya telah diselesaikan

#define CBLAS_INDEX size_t  /* this may vary between platforms */
#ifdef MKL_ILP64
#define MKL_INT MKL_INT64
#else
#define MKL_INT int
#endif
CBLAS_INDEX cblas_isamax(const MKL_INT N, const float  *X, const MKL_INT incX);

atau serupa

#ifdef OPENBLAS_USE64BITINT
typedef BLASLONG blasint;
#else
typedef int blasint;
#endif
#define CBLAS_INDEX size_t
CBLAS_INDEX cblas_isamax(OPENBLAS_CONST blasint n, OPENBLAS_CONST float  *x, OPENBLAS_CONST blasint incx);

vs referensi

#ifdef WeirdNEC
   #define CBLAS_INDEX long
#else
   #define CBLAS_INDEX int
#endif
CBLAS_INDEX cblas_isamax(const CBLAS_INDEX N, const float  *X, const CBLAS_INDEX incX);

Kenapa mereka menyimpang dari referensi di sini? Apakah ada komunikasi tentang itu? Juga ... Saya melihat MKL dan OpenBLAS mendefinisikan sejumlah fungsi yang bahkan bukan bagian dari referensi CBLAS:

CBLAS_INDEX cblas_isamin(const MKL_INT N, const float  *X, const MKL_INT incX);
CBLAS_INDEX cblas_idamin(const MKL_INT N, const double *X, const MKL_INT incX);
CBLAS_INDEX cblas_icamin(const MKL_INT N, const void   *X, const MKL_INT incX);
CBLAS_INDEX cblas_izamin(const MKL_INT N, const void   *X, const MKL_INT incX);

CBLAS_INDEX cblas_isamin(OPENBLAS_CONST blasint n, OPENBLAS_CONST float  *x, OPENBLAS_CONST blasint incx);
CBLAS_INDEX cblas_idamin(OPENBLAS_CONST blasint n, OPENBLAS_CONST double *x, OPENBLAS_CONST blasint incx);
CBLAS_INDEX cblas_icamin(OPENBLAS_CONST blasint n, OPENBLAS_CONST void  *x, OPENBLAS_CONST blasint incx);
CBLAS_INDEX cblas_izamin(OPENBLAS_CONST blasint n, OPENBLAS_CONST void *x, OPENBLAS_CONST blasint incx);

CBLAS_INDEX cblas_ismax(OPENBLAS_CONST blasint n, OPENBLAS_CONST float  *x, OPENBLAS_CONST blasint incx);
CBLAS_INDEX cblas_idmax(OPENBLAS_CONST blasint n, OPENBLAS_CONST double *x, OPENBLAS_CONST blasint incx);
CBLAS_INDEX cblas_icmax(OPENBLAS_CONST blasint n, OPENBLAS_CONST void  *x, OPENBLAS_CONST blasint incx);
CBLAS_INDEX cblas_izmax(OPENBLAS_CONST blasint n, OPENBLAS_CONST void *x, OPENBLAS_CONST blasint incx);

CBLAS_INDEX cblas_ismin(OPENBLAS_CONST blasint n, OPENBLAS_CONST float  *x, OPENBLAS_CONST blasint incx);
CBLAS_INDEX cblas_idmin(OPENBLAS_CONST blasint n, OPENBLAS_CONST double *x, OPENBLAS_CONST blasint incx);
CBLAS_INDEX cblas_icmin(OPENBLAS_CONST blasint n, OPENBLAS_CONST void  *x, OPENBLAS_CONST blasint incx);
CBLAS_INDEX cblas_izmin(OPENBLAS_CONST blasint n, OPENBLAS_CONST void *x, OPENBLAS_CONST blasint incx);

Jadi, memperluas standar adalah satu hal, tetapi size_t vs int tampaknya menjadi masalah serius pada sistem 64 bit. Ini harus diselesaikan dengan cara tertentu. Menurut saya cara Netlib masuk akal: Jenis yang sama seperti yang digunakan untuk indeks. Karena semua menyebut rutinitas Fortran seperti ini pada akhirnya

c     isamaxsub.f
c
c     The program is a fortran wrapper for isamax.
c     Witten by Keita Teranishi.  2/11/1998
c
      subroutine isamaxsub(n,x,incx,iamax)
c
      external isamax
      integer  isamax,iamax
      integer n,incx
      real x(*)
c
      iamax=isamax(n,x,incx)
      return
      end

… menyerahkan alamat size_t untuk iamax, itu sepertinya salah. Saya tidak menemukan implementasi lain selain referensi ini di sumber OpenBLAS. Apakah mereka hanya bodoh mengubah tipe eksternal seperti itu atau apakah saya mengabaikan sesuatu yang sangat mendasar? Apakah ada yang benar-benar menggunakan fungsi-fungsi ini?

Hai semua, Referensi BLAS, referensi CBLAS, referensi LAPACK, dua dari dorongan utama dari proyek-proyek ini adalah (1) algoritma numerik dan (2) mendefinisikan antarmuka umum, implementasi referensi dan rangkaian uji yang berjalan ini. Saya pikir semua orang yang terlibat dalam proyek ini senang melihat dan belajar dari proyek lain (OpenBLAS, MKL, dll.) tentang rekayasa perangkat lunak, praktik terbaik untuk menyebarkan perangkat lunak, dll. Kami harus banyak belajar dari proyek ini. (Dan kami juga belajar banyak dari proyek aljabar linier numerik lainnya!) Bagaimanapun: referensi BLAS, CBLAS, LAPACK dapat menggunakan beberapa peningkatan dalam pengemasan CMake, antarmuka, dan jika OpenBLAS (misalnya) memiliki proses yang lebih baik, itu sangat cocok untuk kami, baik, saya semua mendukung bergerak ke arah model ini.

Untuk menambahkan beberapa konteks, CBLAS lahir dari sebuah komite (Forum Teknis Subprogram Aljabar Linier Dasar) yang bekerja dari tahun 1996 hingga 2000 untuk meninjau kembali BLAS, sebagai bagian dari ini mereka mendefinisikan antarmuka C untuk BLAS. Lihat:
http://www.netlib.org/blas/blast-forum/
Secara khusus lihat:
http://www.netlib.org/blas/blast-forum/cinterface.pdf
Saya percaya bahwa CBLAS yang ditawarkan oleh LAPACK adalah implementasi antarmuka seperti yang didefinisikan oleh Forum Teknis Subprogram Aljabar Linier Dasar 25 tahun yang lalu.

Jika ada saran untuk meningkatkan CBLAS, kirimkan. Saya bisa mencoba menyampaikan ini ke berbagai pemangku kepentingan.

Terima kasih untuk penunjuknya. Jadi bagian yang relevan tampaknya adalah B.2.2 dalam spesifikasi itu, yang mengatakan bahwa BLAS_INDEX biasanya adalah size_t , tetapi mungkin juga dipilih untuk identik dengan tipe integer Fortran (yang ditandatangani) yang digunakan untuk pengindeksan. Terserah implementasinya.

Jadi tampaknya implementasi populer yang dioptimalkan memilih size_t dan referensi Netlib memilih bilangan bulat yang sama yang digunakannya untuk Fortran. Saya melihat salinan cblas.h di berbagai proyek yang menggunakan lib (seperti numpy, mengirimkan header untuk lib eksternal), dengan baris itu

#define CBLAS_INDEX size_t  /* this may vary between platforms */

Di https://github.com/LuaDist/gsl/blob/master/cblas/gsl_cblas.h , ini disertai dengan

/* This is a copy of the CBLAS standard header.
 * We carry this around so we do not have to
 * break our model for flexible BLAS functionality.
 */

Ini terdengar seperti ini berasal dari implementasi referensi, tetapi telah berubah? Melihat 41779680d1f233928b67f5f66c0b239aecb42774 … Saya melihat bahwa sakelar CBLAS_INDEX dengan WeirdNEC telah ada sebelum versi 64 bit. Wow, apakah ini komit baru-baru ini. Sekarang saya melihat bahwa size_t berada di referensi cblas.h hingga 2015, 83fc0b48afd1f9a6d6f8dddb16e69ed7ed0e7242 setelah mengubahnya dan memperkenalkan definisi WeirdNEC. Saya tidak membayangkan bahwa ini sangat baru! Sangat membingungkan.

Saya juga melihat bahwa versi cblas.h sebelumnya menyerahkan int ke panggilan fortran, sekarang CBLAS_INDEX . Ini tampaknya benar sekarang, dengan penggunaan CBLAS_INDEX konsisten sebagai tipe integer dan sakelar untuk 32 atau 64 bit di bagian Fortran.

Tetapi mungkinkah perpustakaan yang dioptimalkan yang mewarisi versi lama cblas.h dengan size_t tetapi menyinkronkan sumber dengan kode CBLAS saat ini dari referensi memiliki bug yang bagus? Bukankah mereka melakukan sesuatu seperti ini untuk kasing 32 bit pada sistem 64 bit?

#include <stdio.h>
#include <stdlib.h>


void ia_max(int a, void *b)
{
    int *ib = (int*)b;
    *ib = a*2;
}


int main(int argc, char **argv)
{
    int i = atoi(argv[1]);
    size_t maxi;
    ia_max(i, &maxi);
    printf("in %d out %ld\n", i, (long)maxi);
    return 0;
}

Hal ini mengakibatkan

$ gcc -O -o t t.c
$ ./t 42
in 42 out 140724603453524

Menginisialisasi nilai size_t ke nol membantu, tetapi mungkin hanya dalam kasus little-endian. Apakah tidak ada yang mendapat masalah untuk ini? Aku harus kehilangan sesuatu.

Untuk menyimpulkan:

  1. Referensi CBLAS memiliki size_t sebagai nilai pengembalian terlebih dahulu.
  2. Itu menggunakan int dalam panggilan sebenarnya ke Fortran.
  3. Hilir (pengguna BLAS, CBLAS yang dioptimalkan) dijalankan dengan header versi lama.
  4. Referensi CBLAS memperkenalkan peretasan WeirdNEC untuk sistem tertentu, mengganti size_t dengan int atau long (cocok dengan sisi Fortran?!)
  5. Antarmuka Referensi CBLAS 64 bit dibangun di atas itu, menggunakan CBLAS_INDEX mana-mana untuk integer default Fortran.
  6. Hilir melakukan hal mereka sendiri dengan dukungan 64 bit, tetapi memisahkannya dari CBLAS_INDEX , yang selalu size_t.
  7. Hilir mewarisi pembungkus CBLAS yang menggunakan CBLAS_INDEX untuk memanggil Fortran yang mengharapkan bilangan bulat default.

Hasilnya, ini terdengar seperti kerusakan yang luar biasa. Header dan kode berbeda. Kenapa belum ada yang memperhatikan masalah? Atau apakah saya melewatkan bagian di mana kode pembungkus CBLAS referensi untuk isamax dan teman-teman tidak benar-benar digunakan?

OpenBLAS setidaknya tidak menggunakan kode pembungkus CBLAS dari Reference-LAPACK (dan tidak pernah melakukannya, sumbernya ada di sana tetapi tidak dibuat)

@martin-frbg Senang mengetahuinya. Bisakah Anda menunjukkan jalur kode untuk, katakanlah, x86-64 yang menunjukkan bagaimana size_t diteruskan ke perhitungan aktual untuk cblas_isamax()? Saya menemukan beberapa implementasi kernel tertentu tetapi saya tidak yakin tentang kasus umum.

Akan lebih baik untuk mengetahui bahwa tidak ada yang benar-benar meneruskan (size_t*) ke antarmuka Fortran.

Yang pasti tidak baik bahwa proyek hanya berasumsi

size_t cblas_isamax(…)

ketika perpustakaan sebenarnya mungkin menawarkan int atau long (atau int64_t) sebagai nilai balik. Mungkin bekerja sebagian besar waktu dengan nilai-nilai dalam register 64 bit, tapi itu tidak bagus. Bisakah kita memperbaiki ini dalam implementasi? Orang-orang tidak memahami contoh Netlib dalam 5 tahun terakhir tentang penggunaan CBLAS_INDEX konsisten.

kode yang relevan ada di OpenBLAS/interface, misalnya interface/imax.c dikompilasi ke cblas_isamax() ketika CBLAS didefinisikan, tidak ada kode Fortran yang terlibat dalam grafik panggilannya.

Ah bagus. Jadi satu kasus yang sebenarnya bermasalah adalah tergantung proyek menggunakan salinan cblas.h yang tidak sesuai dengan perpustakaan.

Saya tidak menemukan penggunaan sebenarnya dari cblas_isamax() dan teman-teman di NumPy (dan SciPy), jadi ini mungkin hanya masalah teoretis. Bagaimanapun itu harus diperbaiki. Jadi:

  1. Lainnya mengikuti contoh Netlib menggunakan int32_t/int64_t (mari kita eksplisit saat itu ;-) BLAS_INDEX untuk pengembalian ukuran dan argumen indeks.
  2. Netlib menyerah dan kembali ke size_t untuk pengembalian itu seperti yang lain.

Apakah ini masalah terpisah untuk dibahas? Itu berhubungan dengan pilihan perpustakaan 32 atau 64 bit.

PS: Saya masih tidak yakin apakah enum di API adalah ide yang bagus (sebagai tipe data aktual untuk argumen fungsi dan anggota struct), karena ada opsi kompiler untuk mengubah integer apa yang digunakan di bawahnya. Tidak begitu relevan dalam praktiknya, tetapi tetap membuat saya tidak nyaman.

Semakin saya memikirkan ini, semakin saya condong ke Opsi 2: Kami memiliki size_t di API untuk waktu yang sangat lama. Kemudian Netlib mengubah size_t itu menjadi int atau long. Terlepas dari apa yang lebih cocok dengan kode Fortran atau mungkin lebih konsisten, size_t didirikan API dan referensi Netlib memecahkannya.

Haruskah saya membuka PR tentang mengubah sesuatu untuk

size_t cblas_isamax(const CBLAS_INDEX N, const float  *X, const CBLAS_INDEX incX);
size_t cblas_idamax(const CBLAS_INDEX N, const double *X, const CBLAS_INDEX incX);
size_t cblas_icamax(const CBLAS_INDEX N, const void   *X, const CBLAS_INDEX incX);
size_t cblas_izamax(const CBLAS_INDEX N, const void   *X, const CBLAS_INDEX incX);

lagi? Seharusnya tidak ada makro pada posisi ini lagi untuk menekankan bahwa itu selalu size_t, di mana-mana, masa lalu dan masa depan.

Di https://github.com/numpy/numpy/issues/19243 kita sekarang pada dasarnya turun ke: "Screw Netlib, size_t berfungsi untuk semua orang".

Ada tiga alasan untuk menggunakan size_t :

  1. Semua fungsi pustaka standar C dan C++ menerima dan mengembalikan nilai ini, misalnya void* malloc(size_t) , size_t strlen() , atau std::size_t std::vector<T>::size() (C++). Menggunakan size_t menghindari pemotongan nilai dan konversi yang ditandatangani/tidak ditandatangani.
  2. size_t sering digunakan untuk menyatakan kuantitas yang tidak boleh negatif, misalnya, dimensi matriks.
  3. Standar C dan C++ menjamin bahwa Anda dapat menyimpan ukuran larik apa pun dalam size_t dan bahwa Anda dapat mengindeks semua elemen dengan size_t , lih. cppference.com: size_t .

Sunting: Anda dapat membangun dengan indeks 64 bit pada sistem 32 bit di mana size_t adalah 32 bit, bukan? Kemudian Anda mendapat overflow.

Tidak karena sistem 32-bit mungkin memiliki lebih dari 4 GB memori virtual (Linux mendukung ini) tetapi satu proses 32-bit tidak pernah dapat mengakses lebih dari 4 GB. Artinya, 32-bit atas dari indeks 64-bit tidak pernah digunakan.

_Batas memori untuk proses 32-bit yang berjalan pada OS Linux 64-bit_

Saya juga berpikir bahwa menjaga size_t adalah hal yang benar untuk dilakukan, karena mengubahnya adalah pemutusan ABI dan membuat Netlib tidak sinkron dengan seluruh dunia.

Tapi saya merasa terdorong untuk mengabaikan argumen Anda ;-)

1. All of the C and C++ standard library functions accept and return this value

Ketika saya meneliti ini, saya menemukan pengakuan bahwa itu adalah kesalahan historis untuk menggunakan tipe yang tidak ditandatangani untuk indeks wadah C++, dan mungkin bahkan tipe kembalinya metode size(), karena Anda dengan cepat akhirnya mencampur nomor yang ditandatangani dan tidak ditandatangani di beberapa cara. Keadaan Netlib saat ini akan konsisten dengan dirinya sendiri, selalu menggunakan jenis yang ditandatangani untuk ukuran dan indeks, tetapi tentu saja tidak konsisten dengan malloc() , yang memiliki persyaratan untuk ukuran yang tidak ditandatangani untuk benar-benar dapat menangani semua memori yang sesuai 32 bit (atau 64 bit, secara teori).

Saya bertanya-tanya sendiri tentang hal itu dalam kode yang saya tulis di mana saya akhirnya menyerahkan indeks sebagai offset ke panggilan fungsi. Indeks tidak ditandatangani, offset ditandatangani. Terlepas dari compiler (MSVC) yang bingung dengan -unsigned_value , ini berarti bahwa saya selalu harus khawatir tentang kemungkinan overflow dalam konversi.

Tapi bagaimanapun, jika itu hanya tentang menghitung ukuran memori untuk diserahkan ke malloc() dan teman-teman, size_t adalah hal yang wajar, dan sudah ada sebelumnya di CBLAS.

Pada kemungkinan masalah dengan status kode saat ini, ketidakcocokan dengan cblas.h di-vendorkan dalam build:

Tidak karena sistem 32-bit mungkin memiliki lebih dari 4 GB memori virtual (Linux mendukung ini) tetapi satu proses 32-bit tidak pernah dapat mengakses lebih dari 4 GB. Artinya, 32-bit atas dari indeks 64-bit tidak pernah digunakan.

Benar, size_t tetap 32 bit. Ketika Anda (mungkin konyol) membangun cblas_isamax() untuk mengembalikan bilangan bulat 64 bit, setelah meretas build untuk tidak menggunakan long , tetapi int64_t , tentu saja, apa yang akan benar-benar terjadi dalam penggunaan seperti itu?

size_t cblas_isamax(); // really int64_t cblas_isamax()!
size_t value = cblas_isamax(…);

Konvensi pemanggilan x86 mungkin memasukkan nilai 64 bit ke dalam EAX dan EDX. Atau mungkin berfungsi dengan pengembalian pointer dan beberapa buffer. Tapi apa yang akan dilakukan arsitektur lain? Jadi Anda mungkin tidak mendapatkan korupsi, tapi pasti nilai yang salah. Kasus terbaik adalah 32 bit yang lebih tinggi diabaikan.

Sekarang bayangkan sistem 32 bit big-endian (semacam bentuk ARM) ... yakin Anda bahkan akan mendapatkan setengah dari nilai yang diinginkan kembali?

Anda tidak dapat benar-benar bekerja dengan data non-jarang yang membutuhkan indeks 64 bit dalam program 32 bit, tentu saja. Tetapi hanya dapat melakukan panggilan fungsi yang tidak cocok yang _at_least_ memberikan hasil yang salah tampaknya tidak sehat.

Saya melakukan beberapa pengujian cepat ... pada x86 Linux ( gcc -m32 pada sistem x86-64), Anda cukup membuang 32 bit teratas.

Kasus yang lebih menarik ... 64 bit size_t:

size_t cblas_isamax(); // really int32_t cblas_isamax()!
size_t value = cblas_isamax(…);

Sekali lagi, pada x86-64, hubungan khusus antara 64 bit RAX dan 32 bit EAX membuat hal-hal agak terdefinisi dengan baik juga hanya diam-diam nol 32 bit atas setelah Anda melakukan operasi 32 bit pada register bersama. Tetapi ada kesenangan untuk dimiliki dengan definisi fungsi yang sedikit aneh:

$ cat ret32.c 
#include <stdint.h>

int32_t ret64(int64_t a)
{
    a += 1LL<<32;
    return a;
}
$ gcc -m64  -g -c -o ret32.o ret32.c 
$ LANG=C objdump -S ret32.o 
[…]
   8:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
    a += 1LL<<32;
   c:   48 b8 00 00 00 00 01    movabs $0x100000000,%rax
  13:   00 00 00 
  16:   48 01 45 f8             add    %rax,-0x8(%rbp)
    return a;
  1a:   48 8b 45 f8             mov    -0x8(%rbp),%rax

Anda dapat berdebat jika kompiler cerdas untuk bekerja pada register 64 bit penuh dan membiarkan 32 bit teratas tidak jelas untuk fungsi yang diharapkan mengembalikan nilai 32 bit, tetapi sangat sah jika Anda hanya mengandalkan penelepon menggunakan 32 bit yang lebih rendah, saya kira.

$ cat call.c 
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

INDEX ret64(int64_t);

int main(int argc, char **argv)
{
    if(argc < 2)
        return 1;
    int64_t a = (int64_t)strtoll(argv[1], NULL, 10);
    INDEX  s = ret64(a);
    printf("%lld\n", (long long)s);
    return 0;
}
$ gcc -m64 -g -DINDEX=int32_t -c -o call32_64.o call.c
$ gcc -m64 -g -DINDEX=size_t -c -o call64_64.o call.c
$ ./call32_64 1
1
$ ./call64_64 1
4294967297

Seru. Nilai pengembalian 32 bit yang memberikan lebih dari apa yang mungkin dalam 32 bit. Inilah yang dapat terjadi (pada prinsipnya) dengan status Netlib CBLAS saat ini yang ditautkan dengan kode yang mengharapkan size_t. Saya kira 32 bit atas RAX akan menjadi nol dalam kode aktual dalam praktiknya. Tapi siapa tahu ... compiler mengharapkan pemanggil untuk tidak menggunakan lebih dari 32 bit yang lebih rendah pada platform apapun ... mungkin juga menyimpan sampah di sana.

Jadi … apakah kita setuju untuk memindahkan Netlib kembali ke size_t sebagai nilai pengembalian?

Terima kasih untuk semua komentar berharga ini!

Saya membahas topik ini sedikit dengan @langou. Berdasarkan pembahasan di sini, usulan saya adalah:

Dalam PR terpisah:

  1. Kami kembali ke cblas.h yang menggunakan dua definisi bilangan bulat, katakanlah CBLAS_INDEX dan CBLAS_INT. Itulah yang terjadi di MKL (CBLAS_INDEX dan MKL_INT) dan OpenBLAS (CBLAS_INDEX dan blasint). CBLAS_INDEX hanya akan digunakan dalam pengembalian i*amax . Dengan itu, kami mengembalikan ABI yang kompatibel dengan BLAS lainnya.
  2. Selain itu, kami memilih nilai default CBLAS_INDEX menjadi size_t dan mengumpulkan opini dari komunitas.

Saya pikir ini sejalan (atau mungkin sama) ide di balik diskusi baru-baru ini di utas ini.
Seperti yang ditunjukkan @drhpc ,
https://github.com/Reference-LAPACK/lapack/commit/83fc0b48afd1f9a6d6f8dddb16e69ed7ed0e7242 mengubah nilai default CBLAS_INDEX, dan
https://github.com/Reference-LAPACK/lapack/commit/41779680d1f233928b67f5f66c0b239aecb42774 mengubah penggunaan CBLAS_INDEX.

Hanya untuk memperkuat:

  • OpenBLAS, MKL, GNU Scientific Library, dan Numpy semuanya menggunakan size_t secara default.
  • Antarmuka C untuk BLAS (https://www.netlib.org/blas/blast-forum/cinterface.pdf) menunjukkan bahwa, biasanya, CBLAS_INDEX = size_t .

Apa kamu setuju? Jika Anda melakukannya, saya dapat membuka PR. Atau mungkin @drhpc ingin melakukan itu.

Saya setuju. Dan tolong lanjutkan PR-nya.

@mgates3 menyebutkan kepada saya diskusi di Grup Google Slate:
https://groups.google.com/a/icl.utk.edu/g/slate-user/c/f5y6gt0aoLs/m/oQyyhikwCgAJ
Diskusinya bukan tentang "CBLAS_INDEX" seharusnya, tetapi lebih pada "CBLAS_INT" seharusnya. Haruskah CBLAS_INT menjadi size_t atau integer yang ditandatangani atau lainnya? Saya pikir peserta membuat poin bagus jadi saya meneruskan.

Silakan, lihat #588.

Benar, size_t tetap 32 bit. Ketika Anda (mungkin konyol) membangun cblas_isamax() untuk mengembalikan bilangan bulat 64 bit, setelah meretas build untuk tidak menggunakan long , tetapi int64_t , tentu saja, apa yang akan benar-benar terjadi dalam penggunaan seperti itu?

size_t cblas_isamax(); // really int64_t cblas_isamax()!
size_t value = cblas_isamax(…);

Konvensi pemanggilan x86 mungkin memasukkan nilai 64 bit ke dalam EAX dan EDX. Atau mungkin berfungsi dengan pengembalian pointer dan beberapa buffer. Tapi apa yang akan dilakukan arsitektur lain? Jadi Anda mungkin tidak mendapatkan korupsi, tapi pasti nilai yang salah. Kasus terbaik adalah 32 bit yang lebih tinggi diabaikan.

Sekarang bayangkan sistem 32 bit big-endian (semacam bentuk ARM) ... yakin Anda bahkan akan mendapatkan setengah dari nilai yang diinginkan kembali?

Ini adalah permainan berakhir. Pada CPU Arm 32-bit, empat nilai 32-bit dapat dilewatkan dan dikembalikan dalam register, nilai 64-bit menempati dua register berturut-turut, lihat Bagian 6.1.1.1 dalam _Procedure Call Standard for the Arm Architecture_ . Alih-alih menulis ke satu register, callee akan mengacaukan dua register dengan bilangan bulat 64-bitnya; ini jelas menjadi masalah. Segera setelah pemanggil kehabisan register untuk parameter, tumpukan digunakan. Penjajaran tumpukan adalah 32 bit tetapi alih-alih membaca atau menulis 32 bit, callee menulis 64 bit; sekali lagi, ini adalah permainan berakhir dan masalah ini (ketidakcocokan ukuran baca/tulis tumpukan) akan menyebabkan masalah pada semua arsitektur set instruksi di beberapa titik.

Saya bertanya-tanya sendiri tentang hal itu dalam kode yang saya tulis di mana saya akhirnya menyerahkan indeks sebagai offset ke panggilan fungsi. Indeks tidak ditandatangani, offset ditandatangani. Terlepas dari compiler (MSVC) yang bingung dengan -unsigned_value, ini berarti bahwa saya selalu harus khawatir tentang kemungkinan overflow dalam konversi.

Tidak, komite standar di belakang C dan C++ membuat kode Anda berperilaku dengan cara yang jelas dalam kasus ini: Jika u adalah nilai yang tidak ditandatangani dan s adalah nilai yang ditandatangani, di mana u memiliki setidaknya bit sebanyak s , maka u + s akan menghasilkan hasil yang benar secara matematis kecuali u + s over- atau underflows. Jika under-/overflows, hasilnya akan membungkus, yaitu, (u + s) mod 2^b , di mana b adalah jumlah bit dalam u dan s . Di sisi lain, jika jenis yang ditandatangani dapat mewakili semua nilai dari jenis yang tidak ditandatangani, maka nilai yang tidak ditandatangani akan dikonversi ke jenis yang tidak ditandatangani.

Klausul yang relevan dalam draft standar C11 adalah sebagai berikut:

  • 6.2.5.9: Operasi biner dengan hanya operan yang tidak ditandatangani tidak dapat meluap; hasilnya diambil modulo MAX + 1 , di mana MAX adalah nilai representable terbesar.
  • 6.3.1.3: Diberikan nilai yang ditandatangani s , itu dikonversi ke nilai yang tidak ditandatangani s jika s >= 0 , jika tidak maka dikonversi ke s + MAX + 1 .
  • 6.3.1.8: Operand yang ditandatangani dan tidak ditandatangani [dengan ukuran yang sama] dikonversi menjadi tidak ditandatangani; operan yang tidak ditandatangani dikonversi ke tipe yang ditandatangani jika tipe yang ditandatangani dapat mewakili semua nilai dari tipe yang tidak ditandatangani

Oleh karena itu, u + s (sintaks C) akan dievaluasi ke

  • (u + s) mod (M + 1) jika s >= 0 ,
  • (u + s + M + 1) mod (M + 1) sebaliknya.

Dengan tidak adanya over- atau underflow, ekspresi ini akan mengevaluasi ke u + s yang merupakan hasil yang diinginkan secara intuitif.

Ketika saya meneliti ini, saya menemukan pengakuan bahwa itu adalah kesalahan historis untuk menggunakan tipe yang tidak ditandatangani untuk indeks wadah C++, dan mungkin bahkan tipe kembalinya metode size(), karena Anda dengan cepat akhirnya mencampur nomor yang ditandatangani dan tidak ditandatangani di beberapa cara.

Ada beberapa programmer C++ (termasuk penemu C++) yang mengusulkan untuk menggunakan bilangan bulat yang ditandatangani di mana-mana, lihat Pedoman Inti C++ tetapi saya tidak akan menyebut ini sebagai pengakuan. Masalah dengan kebijakan "bilangan bulat yang ditandatangani di mana-mana" adalah

  • memeriksa nilai minimum: dengan bilangan bulat yang tidak ditandatangani, dalam banyak kasus, pemeriksaan nilai minimum itu berlebihan, dengan bilangan bulat yang ditandatangani itu wajib; ini rawan kesalahan dan dapat menyebabkan masalah keamanan, lihat misalnya CWE-839 _Perbandingan Rentang Angka Tanpa Pemeriksaan Minimum_ .
  • overflows: overflow yang tidak ditandatangani memiliki hasil yang terdefinisi dengan baik sedangkan overflow integer yang ditandatangani merupakan perilaku yang tidak terdefinisi.

Anda dapat mencoba memeriksa overflow yang ditandatangani dengan ekspresi a + b < a tetapi kompiler mungkin mengoptimalkannya tanpa peringatan, lihat misalnya bug GCC 30475 _assert(int+100 > int) dioptimalkan jauh_ dari 2007. Ini akan berhasil dengan bilangan bulat yang tidak ditandatangani ( a tidak ditandatangani, b mungkin ditandatangani dan b memiliki bit sebanyak a ). Melihat artikel _OptOut – Compiler Undefined Behavior Optimizations_ dari tahun 2020, perilaku GCC tampaknya tidak berubah.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

christoph-conrads picture christoph-conrads  ·  26Komentar

Dichloromethane picture Dichloromethane  ·  11Komentar

miroi picture miroi  ·  10Komentar

weslleyspereira picture weslleyspereira  ·  5Komentar

oxydicer picture oxydicer  ·  6Komentar