Lapack: Autoriser une installation possible de la bibliothÚque index-64 aux cÎtés de la bibliothÚque index-32 standard ?

CrĂ©Ă© le 1 nov. 2020  Â·  41Commentaires  Â·  Source: Reference-LAPACK/lapack

Actuellement, debian a (certains) en-tĂȘtes sĂ©parĂ©s pour blas64 et cblas64 (mais pas de l'implĂ©mentation de rĂ©fĂ©rence).

Je ne sais pas s'ils sont corrects ou non, en ce qui concerne l'API index64 de référence (ils proviennent de la bibliothÚque blis).

Serait-il possible d'ajouter une option à cmake, quelque chose comme BUILD_INDEX64 , qui est par défaut OFF , mais si elle est activée, elle créera les bibliothÚques index-64 ?
Si je fais un PR pour une telle option, cela sera-t-il considéré comme une possibilité ?

Certaines choses que j'avais Ă  l'esprit pour permettre Ă  cela de coexister avec l'installation standard - nommez les bibliothĂšques en libblas64.so, libcblas64.so, liblapack64.so, liblapacke64.so , de cette façon, il n'y a pas de conflit entre les noms de bibliothĂšque (bien sĂ»r, vous ne pouvez pas lier avec libblas et libblas64 en mĂȘme temps).
De plus, la bibliothĂšque devrait ĂȘtre compilĂ©e deux fois, une fois pour index32 et une fois pour index64 (mais c'est un scĂ©nario parfaitement normal et non un briseur d'affaire).
Le seul conflit auquel je ne parviens pas Ă  trouver une bonne rĂ©solution est le nom des fichiers d'en-tĂȘte.
Si vous suivez le style debian, il peut ĂȘtre judicieux d'appeler Ă©galement les en-tĂȘtes c se terminant par 64.
(Je maintiens cela pour Gentoo et j'aimerais garder l'écosystÚme trÚs proche de Debian, afin que nous ayons un minimum de problÚmes pour les développeurs qui passent d'un systÚme à l'autre)

Je suis ouvert Ă  toutes suggestions avant de faire le PR :heart:

Merci,
AĂŻcha

Build System Enhancement

Tous les 41 commentaires

Salut Aisha, cela me semble tout Ă  fait logique, mais voyons si nous avons des commentaires des autres. Attendons donc quelques jours. J

TrĂšs certainement, cela semble ĂȘtre un bon plan.

OMG @langou
tu es si rapide :coeur:

Juste pour ĂȘtre complet, j'Ă©cris les choses que nous avons encore Ă  faire :

  • DĂ©couvrez comment nommer les en-tĂȘtes afin que l'API 32 bits puisse coexister avec l'API 64 bits
  • Corrigez les instructions printf/fprintf afin qu'elles utilisent le qualificateur correct pour l'impression.

Toutes les suggestions pour résoudre le premier point sont les bienvenues, je n'ai malheureusement pas de solution "propre".

Quelques questions, qui m'aideront à gérer le nommage des fichiers

  • Il semble y avoir une tonne de dĂ©finitions en double entre cblas_f77.h et cblas_test.h . Avons-nous vraiment besoin de ça ?
  • cblas_test.h doit-il ĂȘtre installé ? Compte tenu de son nom (et des fichiers dans lesquels il est utilisĂ©), je suppose qu'il ne sera utilisĂ© que pendant la phase de test. Peut-ĂȘtre ne devrions-nous pas installer ce fichier au niveau du systĂšme ?

Salut @epsilon-0,

Certaines choses que j'avais en tĂȘte pour permettre Ă  cela de coexister avec l'installation standard - nommez les bibliothĂšques en libblas64.so, libcblas64.so, liblapack64.so, liblapacke64.so, de cette façon il n'y a pas de conflit entre les noms de bibliothĂšque ( bien sĂ»r, vous ne pouvez pas lier Ă  la fois libblas et libblas64 en mĂȘme temps).

vous cherchez peut-ĂȘtre le PR #218. L'auteur de ce PR est Björn Esser du projet Fedora.

Salut @epsilon-0. Le #462 a-t-il résolu ce problÚme ?

@weslleyspereira non, ce n'est pas encore terminé.
Il doit y avoir un peu plus de renommage/gestion des en-tĂȘtes.
Je suis occupé pour les prochaines semaines donc je ne pourrai pas le faire bientÎt.
Aperçu de base

  • les fichiers d'en-tĂȘte doivent ĂȘtre appelĂ©s cblas.h et cblas64.h , de mĂȘme pour les autres en-tĂȘtes

    • cela signifie que les fichiers *.c auraient besoin d'un lĂ©ger ajustement pour inclure l'en-tĂȘte appropriĂ©, mais ce n'est que pendant le temps de construction, il peut donc ĂȘtre piratĂ©.

  • les fichiers cmake doivent ĂȘtre installĂ©s sous lapack64 ou cblas64 , etc.

OK je vois. Merci pour le suivi rapide !

J'ai des problĂšmes similaires en essayant d'emballer des choses avec pkgsrc. J'aimerais avoir une installation complĂšte de la rĂ©fĂ©rence, avec cblas et lapacke. Pour diffĂ©rentes implĂ©mentations installĂ©es en mĂȘme temps, je me suis contentĂ© de noms de bibliothĂšques et de sous-rĂ©pertoires diffĂ©rents pour les en-tĂȘtes, donc par exemple

/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)

(et ainsi de suite)

Nous ne considérons pas le changement d'exécution comme les distributions binaires, donc c'est OK si chaque cblas.h (et lapacke.h) est spécifique à sa bibliothÚque correspondante, comme avec des noms supplémentaires pour libopenblas. La sélection du temps de construction se fait via

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

(etc.) C'est ce que les fichiers .pc sont censĂ©s dire, et c'est beaucoup plus facile que de communiquer un nom de fichier d'en-tĂȘte diffĂ©rent. Ils ne sont pas encore cohĂ©rents sur ce point, mais je suis en train de le rĂ©parer. Il semble que les gens aient juste piratĂ© cela dans leurs distributions, mĂȘme s'ils se soucient de toutes les bibliothĂšques de rĂ©fĂ©rence.

J'ai une question sur ces en-tĂȘtes, cependant.

Je pirate la version cmake pour que chaque composant soit construit sĂ©parĂ©ment et j'essaie une autre correction (voir https://github.com/Reference-LAPACK/lapack/pull/556). Je reçois les bibliothĂšques libblas.so et libblas64.so bien construites, je fais configurer les rĂ©pertoires d'en-tĂȘte
 mais les cblas.h et lapacke.h installĂ©s sont identiques pour les versions d'indexation 32 et 64 bits. C'est en contradiction avec openblas : lĂ , j'ai une diffĂ©rence cruciale que je ne vois pas pour les builds 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.*/

Pour les bibliothĂšques de rĂ©fĂ©rence, tous les en-tĂȘtes des versions d'index 32 et 64 bits sont identiques et apparemment, les utilisateurs sont censĂ©s mettre
-DWeirdNEC dans leurs drapeaux (c'Ă©tait peut-ĂȘtre drĂŽle il y a 30 ans) pour cblas.h et -DLAPACK_ILP64 -DHAVE_LAPACK_CONFIG_H . Étant donnĂ© que les gens utilisent les bibliothĂšques BLAS optimisĂ©es en production, la norme de facto n'est pas de les exposer aux utilisateurs. Ceux-ci renvoient Ă  la rĂ©fĂ©rence, Ă  mon humble avis, et aux en-tĂȘtes installĂ©s Ă  partir d'une version ILP64 ne devraient pas nĂ©cessiter d'indicateurs gĂ©niaux pour Ă©viter de planter votre application lors de la liaison Ă  la bibliothĂšque 64 bits.

Sommes-nous d'accord pour dire que c'est la bonne solution pour modifier les en-tĂȘtes au moment de la construction pour dĂ©finir les bons entiers ?

Btw, les fichiers de configuration cblas qui sont installĂ©s manquent Ă©galement de rĂ©fĂ©rence aux defs nĂ©cessaires, ils sont donc cassĂ©s pour les builds d'index 64 bits, comme il semble. Mais en fait, je pense ne pas les installer du tout. Ils sont redondants avec les fichiers .pc et rendent peut-ĂȘtre plus difficile de convaincre les packages dĂ©pendants utilisant cmake d'accepter un choix de packager via BLAS_LIBS etal.

PS : Avec Intel MKL, il y a un switch central -DMKL_ILP64 à régler. J'imagine la mise en place triviale
include/intel-mkl64/cblas.h avec

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

pour s'adapter au schéma général. Je pourrais également mettre la définition dans BLAS_INCLUDES, idem pour les définitions étranges de netlib. Qu'est-ce qui est mieux? Voulons-nous le faire comme Intel ou comme OpenBLAS ?

Sommes-nous d'accord pour dire que c'est la bonne solution pour modifier les en-tĂȘtes au moment de la construction pour dĂ©finir les bons entiers ?

Oui. Je suis d'accord avec cela et prĂ©fĂšre la solution qui ne rĂ©plique pas l'intĂ©gralitĂ© de l'en-tĂȘte. Je pense que c'est plus propre.

Btw, les fichiers de configuration cblas qui sont installés manquent également de référence aux defs nécessaires, ils sont donc cassés pour les builds d'index 64 bits, comme il semble.

Droit. Je viens d'installer les bibliothÚques 64 bits (BUILD_INDEX64=ON) et je n'ai rien vu me disant d'utiliser WeirdNEC , LAPACK_ILP64 ou HAVE_LAPACK_CONFIG_H . Merci de l'avoir remarqué !

Oui. Je suis d'accord avec cela et prĂ©fĂšre la solution qui ne rĂ©plique pas l'intĂ©gralitĂ© de l'en-tĂȘte. Je pense que c'est plus propre.

C'est ambigu pour moi. Quelle est la solution la plus propre ? Ce que je prépare maintenant est tel :

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

Le CMakeFile doit remplacer HAVE_ILP par 1 ou 0, l'en-tĂȘte rĂ©sultant Ă©tant installĂ© pour la version actuelle.

(Au fait : long ne fonctionnerait pas sur Windows. C'est long là-bas
 ou int64_t sur toutes les plateformes avec stdint.)

Droit. Je viens d'installer les bibliothÚques 64 bits (BUILD_INDEX64=ON) et je n'ai rien vu me disant d'utiliser WeirdNEC , LAPACK_ILP64 ou HAVE_LAPACK_CONFIG_H . Merci de l'avoir remarqué !

J'imagine un futur oĂč tu fais

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

Et les choses sont gérées dans foo/include/netlib64/cblas.h, sinon par foo/include/netlib/cblas.h (éventuellement lié à foo/include/cblas.h).

J'ai le soupçon que ce n'est _pas_ ce que vous vouliez dire, mais je veux convaincre que c'est mieux ;-)

Vous pouvez essayer de ne pas dupliquer l'en-tĂȘte en plaçant 'l'en-tĂȘte' dans /foo/include/cblas.h et en faisant en sorte que /foo/include/netlib64/cblas.h inclue celui-ci uniquement en dĂ©finissant WeirdNEC, mais cela signifie que le 64 Les packages bit et 32 ​​bits partagent ce fichier d'en-tĂȘte commun, ce qui est compliquĂ© pour l'emballage. C'est bien mieux si chacun met son fichier dans des endroits/noms sĂ©parĂ©s. Le nom doit rester cblas.h car vous ne voulez pas remplacer les lignes #include <cblas.h> .

Edit: De plus, avoir cblas.h inclus ../cblas.h est dĂ©sordonnĂ© en soi. Nous dĂ©finissons Ă©galement le rĂ©pertoire d'installation de l'en-tĂȘte _one_ pour cmake. Par dĂ©faut, c'est /foo/include, pas /foo/netlib64/include. Je ne vais pas changer cette valeur par dĂ©faut. Les conditionneurs devront spĂ©cifier le sous-rĂ©pertoire comme ceci (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}

Un bel aspect de l'expédition / de l'installation du cblas.h 32 bits avec cette modification de l'emplacement habituel est que la mécanique d'origine fonctionne toujours. Seule la variante 64 bits appliquera WeirdNEC. Vous pouvez décider d'installer uniquement le 64 bits dans un préfixe et de ne pas toucher aux autres parties de l'écosystÚme.

Oh, allez
 le CBLAS/cmake/cblas-config-install.cmake.in semble oublier -DCMAKE_INSTALL_INCLUDEDIR, n'est-ce pas ?

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

(Le commentaire est du sucre sur le dessus.)

J'ai l'impression que la version CMake est beaucoup moins mature qu'on pourrait le penser. Le projet est-il sérieux d'avoir cela comme construction principale ou s'agit-il simplement d'une contribution d'entraßnement ? Je suis vraiment tenté de plutÎt réparer le Makefile à l'ancienne, moins de tracas tout autour. Mais j'ai maintenant passé tellement de temps à réparer les trucs CMake, que je déteste de toute façon. J'aimerais donc en finir.

Je dois abandonner maintenant
 J'ai réussi à déplacer cblas.h vers cblas.h.in comme indiqué ci-dessus, et j'ai ajouté

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)

Ă  CBLAS/include/CMakeLists.txt, ayant Ă©galement dĂ©fini @HAVE_ILP64@ Ă  1 ou 0 dans le CMakeLists.txt de niveau supĂ©rieur. Mais pour ma vie, je ne peux pas comprendre comment faire en sorte que les Ă©lĂ©ments d'installation qui se trouvent dans un fichier CMakeLists.txt de niveau supĂ©rieur installent les en-tĂȘtes gĂ©nĂ©rĂ©s, ou la copie Ă©trange de la mĂȘme chose Ă  partir de ${LAPACK_BINARY_DIR}/include (vraiment ? Un copier dans l'arborescence des sources ?)

Qu'est-ce que la macro append_subdir_files est censĂ©e faire ? Il semble ajouter une copie du prĂ©fixe aux chemins d'en-tĂȘte. Je n'ai pas assez ou trop de chemin vers les fichiers d'en-tĂȘte source. Je veux juste installer les fichiers d'en-tĂȘte d'ICI Ă  LÀ, bon sang.

Quelqu'un de bien informé peut-il aider ici? Je suppose que je pourrais le comprendre demain, mais je ne suis pas sûr que ce soit sans briser quelque chose dans le monde réel pour un soulagement émotionnel.

Oui. Je suis d'accord avec cela et prĂ©fĂšre la solution qui ne rĂ©plique pas l'intĂ©gralitĂ© de l'en-tĂȘte. Je pense que c'est plus propre.

C'est ambigu pour moi. Quelle est la solution la plus propre ? Ce que je prépare maintenant est tel :

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

Le CMakeFile doit remplacer HAVE_ILP par 1 ou 0, l'en-tĂȘte rĂ©sultant Ă©tant installĂ© pour la version actuelle.

(Au fait : long ne fonctionnerait pas sur Windows. C'est long là-bas
 ou int64_t sur toutes les plateformes avec stdint.)

Droit. Je viens d'installer les bibliothÚques 64 bits (BUILD_INDEX64=ON) et je n'ai rien vu me disant d'utiliser WeirdNEC , LAPACK_ILP64 ou HAVE_LAPACK_CONFIG_H . Merci de l'avoir remarqué !

J'imagine un futur oĂč tu fais

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

Et les choses sont gérées dans foo/include/netlib64/cblas.h, sinon par foo/include/netlib/cblas.h (éventuellement lié à foo/include/cblas.h).

J'ai le soupçon que ce n'est _pas_ ce que vous vouliez dire, mais je veux convaincre que c'est mieux ;-)

DĂ©solĂ©, laissez-moi vous expliquer. Au dĂ©but, j'aimais l'idĂ©e de conserver l'en-tĂȘte d'origine cblas.h et de crĂ©er include/netlib64/cblas.h et include/netlib/cblas.h avec quelque chose comme

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

Vous pouvez essayer de ne pas dupliquer l'en-tĂȘte en plaçant 'l'en-tĂȘte' dans /foo/include/cblas.h et en faisant en sorte que /foo/include/netlib64/cblas.h inclue celui-ci uniquement en dĂ©finissant WeirdNEC, mais cela signifie que le 64 Les packages bit et 32 ​​bits partagent ce fichier d'en-tĂȘte commun, ce qui est compliquĂ© pour l'emballage. C'est bien mieux si chacun met son fichier dans des endroits/noms sĂ©parĂ©s. Le nom doit rester cblas.h car vous ne voulez pas remplacer les lignes #include <cblas.h> .

Edit: De plus, avoir cblas.h inclus ../cblas.h est dĂ©sordonnĂ© en soi. Nous dĂ©finissons Ă©galement le rĂ©pertoire d'installation de l'en-tĂȘte _one_ pour cmake.

mais oui, nous devrions utiliser include/netlib64 et include dans les rĂ©pertoires d'inclusion si un en-tĂȘte inclut l'autre.

Par défaut, c'est /foo/include, pas /foo/netlib64/include. Je ne vais pas changer cette valeur par défaut. Les conditionneurs devront spécifier le sous-répertoire comme ceci (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}

Cela me semble bon. Ainsi, vous n'ajouteriez qu'une alternative pour compiler LAPACK sans avoir à _deviner_ les drapeaux du compilateur. Mais la méthode actuelle fonctionnerait aussi.

(Au fait : long ne fonctionnerait pas sur Windows. C'est long là-bas
 ou int64_t sur toutes les plateformes avec stdint.)

Bon Ă  savoir. BLAS++ et LAPACK++ utilisent int64_t au lieu de long long.

@weslleyspereira Donc vous avez d'abord aimé cette idée :

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

avec /prefix/include/cblas.h et /prefix/include/netlib64/cblas.h, ce dernier localisant le premier ? Mais ĂȘtes-vous d'accord maintenant qu'il s'agit d'une solution plus robuste pour installer un en-tĂȘte qui ressemble Ă  ceci pour une version 64 bits ?

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

(long vs. int64 est une autre affaire, mais je suis tout Ă  fait pour faire ce changement, tout comme BLAS++)

Zut, je ne suis mĂȘme pas sĂ»r s'il est sĂ»r de supposer que `#include ".../cblas.h" ne trouvera que l'autre en-tĂȘte indentĂ©. La norme C semble dire que l'ordre de recherche est dĂ©fini par l'implĂ©mentation, pas nĂ©cessairement par rapport Ă  l'en-tĂȘte actuel. Mon principal problĂšme en tant que packager est que j'aurais besoin d'un package sĂ©parĂ© pour cet en-tĂȘte commun ou que le package 64 bits dĂ©pende du package 32 bits uniquement pour cela. Ce serait nul.

J'aimerais vraiment aller de l'avant avec un tel changement pour pkgsrc, pour rĂ©gler un changement pour le code en amont plus tard. Nous pourrions discuter d'un nouveau symbole pour forcer les indices 32 bits ou 64 bits explicitement avec l'un des en-tĂȘtes ( -DNETLIB_INDEX_BITS=64 ?), en utilisant par dĂ©faut ce avec quoi la bibliothĂšque a Ă©tĂ© construite.

Puis-je me mettre d'accord sur la solution que nous envisageons ?

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

et

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

Chaque version du code LAPACK gĂ©nĂšre des en-tĂȘtes qui, au moins par dĂ©faut, correspondent aux bibliothĂšques installĂ©es sans que l'utilisateur ne dĂ©finisse quoi que ce soit. D'ACCORD?

Je pourrais alors le glisser avant la prochaine version de pkgsrc (la date limite approche) et nous pourrons discuter davantage des dĂ©tails de cette implĂ©mentation afin que je puisse supprimer les correctifs aprĂšs avoir fusionnĂ© quelque chose ici, avec une nouvelle version de LAPACK. Avec ce changement, la version simple de Makefile doit Ă©galement ĂȘtre corrigĂ©e, mais je n'en ai pas encore besoin pour les correctifs _my_ lorsque j'utilise simplement la version CMake.

(J'ai juste besoin de vĂ©rifier mon tempĂ©rament lorsque j'essaie de battre cette Ă©trange construction CMake dans la soumission, oĂč il mĂ©lange les copies d'en-tĂȘte dans les rĂ©pertoires de construction et ne peut ensuite pas les trouver pour l'installation. Ou dĂ©cider si ces fichiers .cmake cassĂ©s ont une utilitĂ© pour nous, peut-ĂȘtre qu'il suffit de les supprimer de l'installation
 nous avons pkg-config !)

N'importe quoi? Je dois admettre que je ne vois pas beaucoup de chance pour une solution diffĂ©rente dans la pratique, car c'est l'exemple donnĂ© par openblas, la principale implĂ©mentation que nous utilisons. Je peux imaginer convaincre Intel d'avoir Ă©galement un sous-rĂ©pertoire pour les en-tĂȘtes d'index 64 bits/32 bits, recouvrant leurs mkl_cblas.h et mkl_lapacke.h. Sinon, je construis un package simple qui ne fournit que ceux-ci.

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

Actuellement, j'ai ajoutĂ© des machines Ă  pkgsrc pour fournir des builds avec la drĂŽle de ligne -DWeirdNEC -DHAVE_LAPACK_CONFIG_H -DLAPACK_ILP64 , avec cblas et cblas64 installant des en-tĂȘtes identiques. Cela pourrait rester ainsi, mais je pense toujours qu'il est logique que l'en-tĂȘte soit configurĂ© pour correspondre Ă  l'ABI de construction.

@weslleyspereira Donc vous avez d'abord aimé cette idée :

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

avec /prefix/include/cblas.h et /prefix/include/netlib64/cblas.h, ce dernier localisant le premier ? Mais ĂȘtes-vous d'accord maintenant qu'il s'agit d'une solution plus robuste pour installer un en-tĂȘte qui ressemble Ă  ceci pour une version 64 bits ?

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

Oui c'est ça. Je suis d'accord avec votre solution d'avoir des sous-dossiers pour les en-tĂȘtes 32 et 64 bits. J'en ai discutĂ© avec @langou , et il Ă©tait Ă©galement convaincu que ce serait une bonne solution.

(long vs. int64 est une autre affaire, mais je suis tout Ă  fait pour faire ce changement, tout comme BLAS++)

Droit. Cela devrait ĂȘtre traitĂ© dans un autre numĂ©ro.

J'aimerais vraiment aller de l'avant avec un tel changement pour pkgsrc, pour rĂ©gler un changement pour le code en amont plus tard. Nous pourrions discuter d'un nouveau symbole pour forcer les indices 32 bits ou 64 bits explicitement avec l'un des en-tĂȘtes ( -DNETLIB_INDEX_BITS=64 ?), en utilisant par dĂ©faut ce avec quoi la bibliothĂšque a Ă©tĂ© construite.

Puis-je me mettre d'accord sur la solution que nous envisageons ?

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

et

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

Oui. Je pense que vous pouvez aller de l'avant et proposer un PR à l'avenir, merci ! Personnellement, je pense qu'un nouveau symbole comme NETLIB_INDEX_BITS est tout à fait logique. Je voudrais juste m'assurer que la valeur par défaut reste 32, et que -DWeirdNEC implique -DNETLIB_INDEX_BITS=64 .

Chaque version du code LAPACK gĂ©nĂšre des en-tĂȘtes qui, au moins par dĂ©faut, correspondent aux bibliothĂšques installĂ©es sans que l'utilisateur ne dĂ©finisse quoi que ce soit. D'ACCORD?

Cela me semble bien.

Je pourrais alors le glisser avant la prochaine version de pkgsrc (la date limite approche) et nous pourrons discuter davantage des dĂ©tails de cette implĂ©mentation afin que je puisse supprimer les correctifs aprĂšs avoir fusionnĂ© quelque chose ici, avec une nouvelle version de LAPACK. Avec ce changement, la version simple de Makefile doit Ă©galement ĂȘtre corrigĂ©e, mais je n'en ai pas encore besoin pour les correctifs _my_ lorsque j'utilise simplement la version CMake.

D'accord! Nous aurons probablement une version LAPACK au deuxiĂšme semestre 2021. Et oui, le Makefile devrait ĂȘtre ajustĂ© en consĂ©quence, et je suis prĂȘt Ă  vous aider.

C'est un peu liĂ©. Il ne faut pas oublier que les en-tĂȘtes pour netlib CBLAS ne sont pas uniquement fournis par netlib
 NumPy utilise toujours son propre en-tĂȘte :

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

Et dans cet en-tĂȘte, il dĂ©finit CBLAS_INDEX=size_t , diffĂ©rent du type entier utilisĂ© pour spĂ©cifier les indices. Il est utilisĂ© uniquement pour les valeurs de retour de certaines fonctions :

$ 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);

La différence:

$ 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);

Je me demande si cela peut causer des problÚmes. Pour Netlib, il n'y a qu'un seul type d'index, tandis que d'autres implémentations utilisent un type de valeur de retour différent pour les fonctions d'index. OpenBLAS donne l'exemple. Ils disent qu'isamax renvoie size_t non signé, mais le wrapper C appelle en fait une fonction Fortran qui renvoie un entier signé systÚmes de bits).

L'implĂ©mentation de rĂ©fĂ©rence a-t-elle un avis Ă  ce sujet ? Je _devine_ qu'il n'y a pas vraiment de problĂšme, car une valeur size_t pourra toujours contenir tout retour non nĂ©gatif d'isamax(). Mais ça sent mauvais. (Edit : vous pouvez construire avec des index 64 bits sur un systĂšme 32 bits oĂč size_t est de 32 bits, non ? Ensuite, vous avez un dĂ©bordement. En plus de la difficultĂ© de convertir size_t * en int * .)

Étant donnĂ© que les implĂ©mentations optimisĂ©es semblent avoir dĂ©cidĂ© de size_t lĂ -bas, la rĂ©fĂ©rence devrait-elle accepter ce fait et suivre ?

Et à quel point est-il dangereux, en fait, de lier numpy avec la référence cblas ?

OpenBLAS donne l'exemple. (...)
Étant donnĂ© que les implĂ©mentations optimisĂ©es semblent avoir dĂ©cidĂ© de size_t lĂ -bas, la rĂ©fĂ©rence devrait-elle accepter ce fait et suivre ?

Je ne peux certainement pas parler pour numpy (ou mkl, etc. d'ailleurs), mais j'hésiterais à prétendre qu'OpenBLAS est normatif sous quelque forme que ce soit, encore moins par rapport à ce qui est (je crois) généralement considéré comme __the__ implémentation de référence .. .

SĂ»r. C'est juste que les gens utilisent OpenBLAS ou MKL dans la pratique et les deux semblent s'ĂȘtre installĂ©s sur

#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);

ou similaire

#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 la référence

#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);

Comment se fait-il qu'ils s'Ă©cartent de la rĂ©fĂ©rence ici ? Y a-t-il eu une communication Ă  ce sujet ? Aussi
 je vois MKL et OpenBLAS dĂ©finir une foule de fonctions qui ne font mĂȘme pas partie de la rĂ©fĂ©rence 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);

Donc, Ă©tendre la norme est une chose, mais size_t vs int semble ĂȘtre un problĂšme sĂ©rieux sur les systĂšmes 64 bits. Cela devrait ĂȘtre rĂ©glĂ© d'une maniĂšre ou d'une autre. Il me semble que la mĂ©thode Netlib est sensĂ©e : MĂȘme type que celui qui est utilisĂ© pour les index. Comme tous appellent des routines Fortran comme celle-ci Ă  la fin

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


 remettre une adresse de size_t pour iamax, cela semble tout simplement faux. Je n'ai pas trouvĂ© d'autre implĂ©mentation que celle de rĂ©fĂ©rence dans les sources OpenBLAS. Sont-ils juste stupides de changer le type externe comme ça ou est-ce que je nĂ©glige quelque chose de trĂšs basique ? Est-ce que quelqu'un utilise rĂ©ellement ces fonctions ?

Salut à tous, Référence BLAS, référence CBLAS, référence LAPACK, deux des axes principaux de ces projets sont (1) les algorithmes numériques et (2) la définition d'interfaces communes, une implémentation de référence et une suite de tests qui va de pair. Je pense que toutes les personnes impliquées dans ces projets sont heureuses de regarder et d'apprendre d'autres projets (OpenBLAS, MKL, etc.) sur l'ingénierie logicielle, les meilleures pratiques pour déployer le logiciel, etc. Nous avons beaucoup à apprendre de ces projets. (Et nous apprenons aussi beaucoup d'autres projets d'algÚbre linéaire numérique !) Quoi qu'il en soit : la référence BLAS, CBLAS, LAPACK peut utiliser certaines améliorations dans son emballage CMake, ses interfaces, et si OpenBLAS (par exemple) a un meilleur processus, c'est bien adapté pour nous, eh bien, je suis tout à fait favorable à l'évolution vers ce modÚle.

Pour ajouter un peu de contexte, le CBLAS est né d'un comité (le Forum technique des sous-programmes d'algÚbre linéaire de base) qui a travaillé de 1996 à 2000 sur la révision du BLAS, dans le cadre de laquelle ils ont défini une interface C pour le BLAS. Voir:
http://www.netlib.org/blas/blast-forum/
Voir notamment :
http://www.netlib.org/blas/blast-forum/cinterface.pdf
Je pense que le CBLAS proposé par LAPACK est une implémentation de l'interface telle que définie par le Forum technique des sous-programmes d'algÚbre linéaire de base il y a 25 ans.

S'il y a des suggestions pour améliorer CBLAS, envoyez-les. Je peux essayer de transmettre cela aux différentes parties prenantes.

Merci pour le pointeur. La partie pertinente semble donc ĂȘtre B.2.2 dans cette spĂ©cification, qui dit que BLAS_INDEX est gĂ©nĂ©ralement size_t , mais peut Ă©galement ĂȘtre choisi pour ĂȘtre identique au type entier Fortran (signĂ©) utilisĂ© pour indexage. C'est Ă  la mise en Ɠuvre.

Il semble donc que les implĂ©mentations optimisĂ©es populaires aient choisi size_t et que la rĂ©fĂ©rence Netlib ait choisi le mĂȘme entier qu'elle utilise pour Fortran. Je vois des copies de cblas.h partout dans divers projets qui utilisent la lib (comme numpy, envoyant un en-tĂȘte pour une lib externe), avec cette ligne

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

Dans https://github.com/LuaDist/gsl/blob/master/cblas/gsl_cblas.h , cela est accompagné de

/* 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.
 */

Cela semble provenir de l'implĂ©mentation de rĂ©fĂ©rence, mais a-t-il changĂ© depuis ? En regardant 41779680d1f233928b67f5f66c0b239aecb42774 
 je vois que le commutateur CBLAS_INDEX avec WeirdNEC Ă©tait lĂ  avant la version 64 bits. Wow, ce commit est-il rĂ©cent. Maintenant, je vois que size_t Ă©tait dans la rĂ©fĂ©rence cblas.h jusqu'en 2015, 83fc0b48afd1f9a6d6f8dddb16e69ed7ed0e7242 l'ayant modifiĂ© et introduit la dĂ©finition WeirdNEC. Je n'imaginais pas que ce soit si rĂ©cent ! Sauvagement dĂ©routant.

Je vois aussi que la version prĂ©cĂ©dente de cblas.h a remis un int Ă  l'appel fortran, maintenant CBLAS_INDEX . Cela semble ĂȘtre correct maintenant, avec une utilisation cohĂ©rente de CBLAS_INDEX comme type entier et le commutateur pour 32 ou 64 bits dans la partie Fortran.

Mais se pourrait-il que les bibliothÚques optimisées qui ont hérité d'une ancienne version de cblas.h avec size_t mais qui synchronisent les sources avec le code CBLAS actuel de la référence aient un joli bug ? Ne font-ils pas quelque chose comme ça pour le cas 32 bits sur un systÚme 64 bits ?

#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;
}

Cela se traduit par

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

L'initialisation de la size_t à zéro aide, mais probablement uniquement dans le cas du petit boutien. Personne n'a d'ennuis pour ça ? Je dois manquer quelque chose.

De conclure:

  1. La référence CBLAS avait le size_t comme valeur de retour en premier.
  2. Il a utilisé int dans l'appel réel à Fortran, cependant.
  3. En aval (utilisateurs BLAS optimisĂ©s, CBLAS) s'exĂ©cutent avec l'ancienne version de l'en-tĂȘte.
  4. La référence CBLAS introduit le hack WeirdNEC pour un systÚme spécifique, remplaçant size_t par int ou long (correspondant au cÎté Fortran ?!)
  5. L'interface CBLAS de référence 64 bits est construite par-dessus, en utilisant CBLAS_INDEX partout pour l'entier par défaut Fortran.
  6. Les avals ont fait leur propre chose avec le support 64 bits, mais en le séparant de CBLAS_INDEX , qui est toujours size_t.
  7. Les avals héritent des wrappers CBLAS qui utilisent CBLAS_INDEX pour appeler Fortran qui attend l'entier par défaut.

Cela ressemble Ă  une merveilleuse casse en consĂ©quence. Les en-tĂȘtes et le code ont divergĂ©. Comment se fait-il que personne n'ait encore remarquĂ© de problĂšmes ? Ou ai-je ratĂ© la partie oĂč le code d'emballage de rĂ©fĂ©rence CBLAS pour isamax et ses amis n'est pas rĂ©ellement utilisĂ© ?

OpenBLAS au moins n'utilise pas le code wrapper CBLAS de Reference-LAPACK (et ne l'a jamais fait, la source est lĂ  mais n'est pas construite)

@martin-frbg Bon à savoir. Pouvez-vous indiquer un chemin de code pour, disons, x86-64 qui montre comment le size_t est transmis au calcul réel pour cblas_isamax() ? J'ai trouvé une implémentation spécifique du noyau mais je ne suis pas sûr du cas général.

Il serait bon de savoir que personne ne passe réellement un (size_t*) à l'interface Fortran.

C'est sûr que ce n'est pas bien que les projets supposent juste

size_t cblas_isamax(
)

lorsque la bibliothÚque réelle peut offrir un int ou long (ou int64_t) comme valeur de retour. Peut fonctionner la plupart du temps avec des valeurs dans des registres 64 bits, mais ce n'est pas agréable. Pouvons-nous rectifier cela dans les implémentations ? Les gens n'ont pas repris l'exemple de Netlib au cours des 5 derniÚres années à propos de l'utilisation constante de CBLAS_INDEX .

le code pertinent se trouve dans OpenBLAS/interface, par exemple interface/imax.c est compilé en cblas_isamax() lorsque CBLAS est défini, aucun code Fortran n'est impliqué dans son graphe d'appels.

Ah bien. Ainsi, le seul cas qui pose problÚme est celui des projets dépendants utilisant une copie de cblas.h qui ne correspond pas à la bibliothÚque.

Je ne trouve pas d'utilisation rĂ©elle de cblas_isamax() et d'amis dans NumPy (et SciPy), donc ce n'est peut-ĂȘtre qu'un problĂšme thĂ©orique. Il devrait ĂȘtre corrigĂ© quand mĂȘme. Donc:

  1. D'autres suivent l'exemple de Netlib en utilisant int32_t/int64_t (soyons explicites ;-) BLAS_INDEX pour les retours de taille et les arguments d'index.
  2. Netlib s'effondre et revient Ă  size_t pour ces retours comme les autres.

Est-ce une question distincte à discuter ? Cela concerne cependant le choix d'une bibliothÚque 32 ou 64 bits.

PS: je ne sais toujours pas si les Ă©numĂ©rations dans l'API sont une bonne idĂ©e (en tant que type de donnĂ©es rĂ©el pour les arguments de fonction et les membres de structure), car il existe des options de compilateur pour modifier l'entier utilisĂ© en dessous. Pas si pertinent dans la pratique, mais me met quand mĂȘme mal Ă  l'aise.

Plus j'y pense, plus je penche pour l'option 2 : Nous avions size_t dans l'API depuis trĂšs longtemps. Ensuite, Netlib a changĂ© ce size_t en int ou long. IndĂ©pendamment de ce qui correspond le mieux au code Fortran ou pourrait ĂȘtre plus cohĂ©rent, l'API size_t a Ă©tĂ© Ă©tablie et la rĂ©fĂ©rence Netlib a cassĂ© cela.

Dois-je ouvrir un PR pour changer les choses pour

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);

de nouveau? Il ne devrait plus y avoir de macro à cette position pour souligner que c'est toujours size_t, partout, passé et futur.

Dans https://github.com/numpy/numpy/issues/19243, nous en sommes maintenant essentiellement arrivés à : « Screw Netlib, size_t fonctionne pour tout le monde ».

Il y a trois raisons d'utiliser size_t :

  1. Toutes les fonctions de bibliothÚque standard C et C++ acceptent et renvoient cette valeur, par exemple, void* malloc(size_t) , size_t strlen() ou std::size_t std::vector<T>::size() (C++). L'utilisation de size_t évite de tronquer les valeurs et les conversions signées/non signées.
  2. size_t est souvent utilisĂ© pour exprimer des quantitĂ©s qui ne peuvent pas ĂȘtre nĂ©gatives, par exemple des dimensions matricielles.
  3. Les standards C et C++ garantissent que vous pouvez stocker la taille de n'importe quel tableau dans un size_t et que vous pouvez indexer tous les éléments avec size_t , cf. cppference.com : size_t .

Edit : Vous pouvez construire avec des index 64 bits sur un systĂšme 32 bits oĂč size_t est de 32 bits, n'est-ce pas ? Ensuite, vous avez un dĂ©bordement.

Non car un systÚme 32 bits peut avoir plus de 4 Go de mémoire virtuelle (Linux le supporte) mais un seul processus 32 bits ne peut jamais accéder à plus de 4 Go. C'est-à-dire que les 32 bits supérieurs des index 64 bits ne sont jamais utilisés.

_Limite de mémoire à un processus 32 bits s'exécutant sur un systÚme d'exploitation Linux 64 bits_

Je pense également que garder size_t est la bonne chose à faire, car le changement a été la rupture ABI et a désynchronisé Netlib avec le reste du monde.

Mais je me sens obligé de pinailler sur vos arguments ;-)

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

Lorsque j'ai fait des recherches Ă  ce sujet, je suis tombĂ© sur l'aveu que c'Ă©tait une erreur historique d'utiliser un type non signĂ© pour les index de conteneur C++, et probablement mĂȘme le type de retour de la mĂ©thode size(), car vous finissez rapidement par mĂ©langer des nombres signĂ©s et non signĂ©s dans d'une certaine maniĂšre. L'Ă©tat actuel de Netlib serait cohĂ©rent avec lui-mĂȘme, utilisant toujours des types signĂ©s pour la taille et les indices, mais bien sĂ»r incohĂ©rent avec malloc() , qui nĂ©cessite une taille non signĂ©e pour pouvoir rĂ©ellement adresser toute la mĂ©moire qui tient dans 32 bits (ou 64 bits, en thĂ©orie).

Je me demande bien Ă  ce sujet dans le code que j'ai Ă©crit oĂč j'ai finalement remis un index comme dĂ©calage Ă  un appel de fonction. L'index est non signĂ©, l'offset signĂ©. En dehors des compilateurs (MSVC) confondus par -unsigned_value , cela signifierait que je dois toujours m'inquiĂ©ter d'un Ă©ventuel dĂ©bordement lors de la conversion.

Mais de toute façon, s'il ne s'agit que de calculer les tailles de mémoire à remettre à malloc() et à ses amis, size_t est la chose naturelle, et cela a déjà été là dans CBLAS.

Sur les problÚmes possibles avec l'état actuel du code, non-concordance avec les cblas.h vendeur dans les builds :

Non car un systÚme 32 bits peut avoir plus de 4 Go de mémoire virtuelle (Linux le supporte) mais un seul processus 32 bits ne peut jamais accéder à plus de 4 Go. C'est-à-dire que les 32 bits supérieurs des index 64 bits ne sont jamais utilisés.

Bon, size_t reste 32 bits. Lorsque vous (aussi idiot que cela puisse ĂȘtre) construit cblas_isamax() pour renvoyer un entier de 64 bits, aprĂšs avoir piratĂ© la compilation pour ne pas utiliser long , mais int64_t , bien sĂ»r, qu'est-ce qui va vraiment arriver dans une telle utilisation?

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

La convention d'appel x86 peut mettre la valeur 64 bits dans EAX et EDX. Ou cela pourrait fonctionner avec un retour de pointeur et un tampon. Mais que feraient les autres architectures ? Donc, vous n'obtiendrez peut-ĂȘtre pas de corruption, mais Ă  coup sĂ»r une mauvaise valeur. Dans le meilleur des cas, les 32 bits supĂ©rieurs sont ignorĂ©s.

Imaginez maintenant un systĂšme 32 bits big-endian (une certaine forme d'ARM)
 vous obtiendrez mĂȘme la moitiĂ© souhaitĂ©e de la valeur renvoyĂ©e ?

Vous ne pouvez pas vraiment travailler avec des données non éparses qui nécessitent des index 64 bits dans le programme 32 bits, bien sûr. Mais le simple fait de pouvoir faire un appel de fonction non correspondant qui _au_moins_ donne des résultats erronés semble malsain.

J'ai fait quelques tests rapides
 sur Linux x86 ( gcc -m32 sur un systÚme x86-64), il suffit de supprimer les 32 bits supérieurs.

Le cas le plus intéressant 
 64 bits size_t :

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

Encore une fois, sur x86-64, la relation particuliÚre entre RAX 64 bits et EAX 32 bits rend les choses quelque peu bien définies pour également mettre à zéro silencieusement les 32 bits supérieurs une fois que vous effectuez une opération 32 bits sur le registre partagé. Mais il y a du plaisir à avoir avec une définition de fonction un peu bizarre :

$ 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

Vous pourriez discuter s'il est intelligent pour le compilateur de travailler sur le registre 64 bits complet et de laisser les 32 bits supérieurs non effacés pour une fonction qui devrait renvoyer une valeur de 32 bits, mais c'est parfaitement légal si vous comptez uniquement sur l'appelant en utilisant les 32 bits inférieurs, je suppose.

$ 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

Amusant. Une valeur de retour 32 bits qui donne plus que ce qui est possible en 32 bits. C'est ce qui peut arriver (en principe) avec l'état actuel de Netlib CBLAS lié à du code qui attend size_t. Je suppose cependant que les 32 bits supérieurs de RAX seront zéro dans le code réel en pratique. Mais qui sait
 le compilateur s'attend à ce que l'appelant n'utilise pas plus que les 32 bits inférieurs sur n'importe quelle plate-forme
 pourrait aussi bien y stocker des déchets.

Alors
 sommes-nous d'accord pour ramener Netlib à size_t comme valeur de retour ?

Merci pour tous ces précieux commentaires !

J'ai discuté un peu de ce sujet avec @langou. Sur la base de la discussion ici, ma proposition est:

Dans un PR séparé :

  1. Nous revenons à un cblas.h qui utilise deux définitions entiÚres, disons CBLAS_INDEX et CBLAS_INT. C'est ce qui se passe dans MKL (CBLAS_INDEX et MKL_INT) et OpenBLAS (CBLAS_INDEX et blasint). CBLAS_INDEX ne sera utilisé que dans le retour de i*amax . Avec cela, nous restaurons une ABI compatible avec les autres BLAS.
  2. De plus, nous choisissons la valeur par dĂ©faut de CBLAS_INDEX pour ĂȘtre size_t et collectons les opinions de la communautĂ©.

Je pense que c'est alignĂ© (ou peut-ĂȘtre la mĂȘme) idĂ©e derriĂšre les rĂ©centes discussions dans ce fil.
Comme @drhpc l'a souligné,
https://github.com/Reference-LAPACK/lapack/commit/83fc0b48afd1f9a6d6f8dddb16e69ed7ed0e7242 a changé la valeur par défaut de CBLAS_INDEX, et
https://github.com/Reference-LAPACK/lapack/commit/41779680d1f233928b67f5f66c0b239aecb42774 a modifié l'utilisation de CBLAS_INDEX.

Juste pour renforcer :

  • OpenBLAS, MKL, GNU Scientific Library et Numpy utilisent tous size_t par dĂ©faut.
  • L'interface C pour BLAS (https://www.netlib.org/blas/blast-forum/cinterface.pdf) indique que, gĂ©nĂ©ralement, CBLAS_INDEX = size_t .

Êtes-vous d'accord? Si vous le faites, je peux ouvrir le PR. Ou peut-ĂȘtre que @drhpc aimerait le faire.

Je suis d'accord. Et s'il vous plaĂźt, continuez avec le PR.

@mgates3 m'a mentionné la discussion sur le groupe Google Slate :
https://groups.google.com/a/icl.utk.edu/g/slate-user/c/f5y6gt0aoLs/m/oQyyhikwCgAJ
La discussion ne porte pas sur ce que devrait ĂȘtre « CBLAS_INDEX », mais plutĂŽt sur ce que devrait ĂȘtre « CBLAS_INT ». CBLAS_INT doit-il ĂȘtre size_t ou un entier signĂ© ou etc. ? Je pense que les participants font de bons points, donc je passe.

S'il vous plaĂźt, voir #588.

Bon, size_t reste 32 bits. Lorsque vous (aussi idiot que cela puisse ĂȘtre) construit cblas_isamax() pour retourner un entier 64 bits, aprĂšs avoir piratĂ© la construction pour ne pas utiliser long , mais int64_t , bien sĂ»r, qu'est-ce qui va vraiment arriver dans une telle utilisation?

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

La convention d'appel x86 peut mettre la valeur 64 bits dans EAX et EDX. Ou cela pourrait fonctionner avec un retour de pointeur et un tampon. Mais que feraient les autres architectures ? Donc, vous n'obtiendrez peut-ĂȘtre pas de corruption, mais Ă  coup sĂ»r une mauvaise valeur. Dans le meilleur des cas, les 32 bits supĂ©rieurs sont ignorĂ©s.

Imaginez maintenant un systĂšme 32 bits big-endian (une certaine forme d'ARM)
 vous obtiendrez mĂȘme la moitiĂ© souhaitĂ©e de la valeur renvoyĂ©e ?

C'est la fin du jeu. Sur les processeurs Arm 32 bits, quatre valeurs 32 bits peuvent ĂȘtre transmises et renvoyĂ©es dans des registres, les valeurs 64 bits occupent deux registres consĂ©cutifs, voir Section 6.1.1.1 dans _Procedure Call Standard for the Arm Architecture_ . Au lieu d'Ă©crire dans un registre, l'appelĂ© encombrera deux registres avec ses entiers de 64 bits ; c'est Ă©videmment un problĂšme. DĂšs que l'appelant n'a plus de registres pour les paramĂštres, la pile est utilisĂ©e. L'alignement de la pile est de 32 bits mais au lieu de lire ou d'Ă©crire sur 32 bits, l'appelĂ© Ă©crit sur 64 bits ; encore une fois, c'est la fin du jeu et ce problĂšme (inadĂ©quation des tailles de lecture/Ă©criture de la pile) devrait causer des problĂšmes sur toutes les architectures de jeux d'instructions Ă  un moment donnĂ©.

Je me demande bien Ă  ce sujet dans le code que j'ai Ă©crit oĂč j'ai finalement remis un index comme dĂ©calage Ă  un appel de fonction. L'index est non signĂ©, l'offset signĂ©. En dehors des compilateurs (MSVC) confondus par -unsigned_value, cela signifierait que je dois toujours m'inquiĂ©ter d'un Ă©ventuel dĂ©bordement lors de la conversion.

Non, les comitĂ©s standard derriĂšre C et C++ font que votre code se comporte de la maniĂšre Ă©vidente dans ce cas : si u est une valeur non signĂ©e et s est une valeur signĂ©e, oĂč u a au moins autant de bits que s , alors u + s donnera le rĂ©sultat mathĂ©matiquement correct Ă  moins que u + s dĂ©borde ou dĂ©borde. S'il dĂ©borde/dĂ©passe, le rĂ©sultat s'enroulera, c'est-Ă -dire (u + s) mod 2^b , oĂč b est le nombre de bits dans u et s . D'un autre cĂŽtĂ©, si le type signĂ© peut reprĂ©senter toutes les valeurs du type non signĂ©, alors la valeur non signĂ©e sera convertie en type non signĂ©.

Les clauses pertinentes du projet de norme C11 sont les suivantes :

  • 6.2.5.9 : Les opĂ©rations binaires avec uniquement des opĂ©randes non signĂ©s ne peuvent pas dĂ©border ; le rĂ©sultat est pris modulo MAX + 1 , oĂč MAX est la plus grande valeur reprĂ©sentable.
  • 6.3.1.3 : Étant donnĂ© une valeur signĂ©e s , elle est convertie en la valeur non signĂ©e s si s >= 0 , sinon elle est convertie en s + MAX + 1 .
  • 6.3.1.8 : Les opĂ©randes signĂ©s et non signĂ©s [de mĂȘme taille] sont convertis en non signĂ©s ; un opĂ©rande non signĂ© est converti en un type signĂ© si le type signĂ© peut reprĂ©senter toutes les valeurs du type non signĂ©

Par conséquent, u + s (syntaxe C) sera évalué à

  • (u + s) mod (M + 1) si s >= 0 ,
  • (u + s + M + 1) mod (M + 1) sinon.

En l'absence de dépassement ou de dépassement, cette expression sera évaluée à u + s qui est le résultat intuitivement souhaité.

Lorsque j'ai fait des recherches Ă  ce sujet, je suis tombĂ© sur l'aveu que c'Ă©tait une erreur historique d'utiliser un type non signĂ© pour les index de conteneur C++, et probablement mĂȘme le type de retour de la mĂ©thode size(), car vous finissez rapidement par mĂ©langer des nombres signĂ©s et non signĂ©s dans d'une certaine maniĂšre.

Il y a des programmeurs C++ (y compris l'inventeur du C++) qui proposent d'utiliser des entiers signés partout, voir les C++ Core Guidelines mais je n'appellerais pas cela un aveu. Le problÚme avec la politique « entiers signés partout » est

  • vĂ©rification des valeurs minimales : avec un entier non signĂ©, il est dans de nombreux cas superflu de vĂ©rifier la valeur minimale, avec un entier signĂ©, c'est obligatoire ; ceci est sujet aux erreurs et peut causer des problĂšmes de sĂ©curitĂ©, voir par exemple CWE-839 _Comparaison de plage numĂ©rique sans contrĂŽle minimum_ .
  • dĂ©bordements : un dĂ©bordement non signĂ© a un rĂ©sultat bien dĂ©fini alors qu'un dĂ©bordement d'entier signĂ© constitue un comportement indĂ©fini.

Vous pouvez essayer de vérifier un débordement signé avec l'expression a + b < a mais le compilateur peut l'optimiser sans avertissement, voir par exemple le bogue GCC 30475 _assert(int+100 > int) optimisé away_ à partir de 2007. Cela fonctionnerait avec des entiers non signés ( a non signé, b éventuellement signé et b ayant au plus autant de bits que a ). En voyant l'article _OptOut - Compiler Undefined Behavior Optimizations_ de 2020, le comportement de GCC n'a apparemment pas changé.

Cette page vous a été utile?
0 / 5 - 0 notes