由于 MPI 不提供二进制兼容性,仅提供源代码兼容性,因此我们不得不将我们的求解器源代码发送给客户,以便他们将我们的求解器与他们首选的 MPI 版本一起使用。好吧,我们已经到了无法再提供源代码的地步。
因此,我正在寻找围绕 MPI 调用创建包装器的方法。我们的想法是为我们提供 stub 函数的 header ,用户将编写实现,从中创建一个动态库,然后我们的求解器将在运行时加载它。
但是解决方案并不“优雅”并且容易出错。因为有 struct 参数(例如,MPI_Request),其 struct 定义可能因一个 MPI 实现而异,我们需要接受 (void*) 用于我们的许多 stub 参数。此外,如果一个 MPI 与另一个 MPI 的参数数量不同(我不确定它是否保证永远不会发生),唯一的解决方法是使用 var_args。
//header (provided by us)
int my_stub_mpi_send(const void buf, int count, void* datatype,
int dest, int tag, void* comm);
//*.c (provided by user)
#include <my_stub_mpi.h>
#include <mpi.h>
int my_stub_mpi_send(const void buf, int count, void* datatype,
int dest, int tag, void* comm)
{
return MPI_Send(buf, count, *((MPI_Datatype) datatype),
dest, tag, ((MPI_Comm) comm));
}
//Notes: (1) Most likely the interface will be C, not C++,
// unless I can make a convincing case for C++;
// (2) The goal here is to avoid *void pointers, if possible;
我的问题是是否有人知道解决这些问题的方法?
最佳答案
如果您只针对支持 PMPI 分析接口(interface)的平台,那么有一个通用解决方案,只需对原始源代码进行最少甚至不需要更改。基本思想是(滥用)使用 PMPI 接口(interface)作为包装器。在某种非 OO 意义上,它可能是桥接模式的实现。
首先,几点观察。 MPI 标准中定义了一种结构类型,即 MPI_Status。它只有三个公开可见的字段:MPI_SOURCE、MPI_TAG 和 MPI_ERR。没有 MPI 函数按值获取 MPI_Status。该标准定义了以下不透明类型:MPI_Aint、MPI_Count、MPI_Offset 和 MPI_Status(+ 几种 Fortran 互操作性类型为清楚起见,特此删除)。前三个是不可或缺的。然后是10种句柄类型,从MPI_Comm到MPI_Win。句柄可以作为特殊整数值或作为指向内部数据结构的指针来实现。 MPICH 和基于它的其他实现采用第一种方法,而 Open MPI 采用第二种方法。作为指针或整数,任何类型的句柄都可以适合单个 C 数据类型,即 intptr_t。
基本思想是覆盖所有 MPI 函数并将它们的参数重新定义为 intptr_t 类型,然后让用户编译的代码转换为正确的类型并进行实际的 MPI 调用:
在 mytypes.h 中:
typedef intptr_t my_MPI_Datatype;
typedef intptr_t my_MPI_Comm;
在mympi.h中:
#include "mytypes.h"
// Redefine all MPI handle types
#define MPI_Datatype my_MPI_Datatype
#define MPI_Comm my_MPI_Comm
// Those hold the actual values of some MPI constants
extern MPI_Comm my_MPI_COMM_WORLD;
extern MPI_Datatype my_MPI_INT;
// Redefine the MPI constants to use our symbols
#define MPI_COMM_WORLD my_MPI_COMM_WORLD
#define MPI_INT my_MPI_INT
// Redeclare the MPI interface
extern int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm);
在mpiwrap.c中:
#include <mpi.h>
#include "mytypes.h"
my_MPI_Comm my_MPI_COMM_WORLD;
my_MPI_Datatype my_MPI_INT;
int MPI_Init(int *argc, char ***argv)
{
// Initialise the actual MPI implementation
int res = PMPI_Init(argc, argv);
my_MPI_COMM_WORLD = (intptr_t)MPI_COMM_WORLD;
my_MPI_INT = (intptr_t)MPI_INT;
return res;
}
int MPI_Send(void *buf, int count, intptr_t datatype, int dest, int tag, intptr_t comm)
{
return PMPI_Send(buf, count, (MPI_Datatype)datatype, dest, tag, (MPI_Comm)comm);
}
在您的代码中:
#include "mympi.h" // instead of mpi.h
...
MPI_Init(NULL, NULL);
...
MPI_Send(buf, 10, MPI_INT, 1, 10, MPI_COMM_WORLD);
...
MPI 包装器可以静态链接或动态预加载。只要 MPI 实现对 PMPI 接口(interface)使用弱符号,这两种方法都有效。您可以扩展上面的代码示例以涵盖所有使用的 MPI 函数和常量。所有常量都应保存在 MPI_Init/MPI_Init_thread 的包装器中。
处理 MPI_Status 有点复杂。尽管该标准定义了公共(public)字段,但并未说明它们在结构中的顺序或位置。再一次,MPICH 和 Open MPI 显着不同:
// MPICH (Intel MPI)
typedef struct MPI_Status {
int count_lo;
int count_hi_and_cancelled;
int MPI_SOURCE;
int MPI_TAG;
int MPI_ERROR;
} MPI_Status;
// Open MPI
struct ompi_status_public_t {
/* These fields are publicly defined in the MPI specification.
User applications may freely read from these fields. */
int MPI_SOURCE;
int MPI_TAG;
int MPI_ERROR;
/* The following two fields are internal to the Open MPI
implementation and should not be accessed by MPI applications.
They are subject to change at any time. These are not the
droids you're looking for. */
int _cancelled;
size_t _ucount;
};
如果您只使用 MPI_Status 从诸如 MPI_Recv 的调用中获取信息,那么将三个公共(public)字段复制到一个用户定义的静态结构中是微不足道的,其中包含只有那些领域。但是,如果您还使用读取非公共(public)函数的 MPI 函数,那是不够的,例如MPI_Get_count。在这种情况下,一种愚蠢的非 OO 方法是简单地嵌入原始状态结构:
在 mytypes.h 中:
// 64 bytes should cover most MPI implementations
#define MY_MAX_STATUS_SIZE 64
typedef struct my_MPI_Status
{
int MPI_SOURCE;
int MPI_TAG;
int MPI_ERROR;
char _original[MY_MAX_STATUS_SIZE];
} my_MPI_Status;
在mympi.h中:
#define MPI_Status my_MPI_Status
#define MPI_STATUS_IGNORE ((my_MPI_Status*)NULL)
extern int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Status *status);
extern int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count);
在mpiwrap.c中:
int MPI_Recv(void *buf, int count, my_MPI_Datatype datatype, int dest, int tag, my_MPI_Comm comm, my_MPI_Status *status)
{
MPI_Status *real_status = (status != NULL) ? (MPI_Status*)&status->_original : MPI_STATUS_IGNORE;
int res = PMPI_Recv(buf, count, (MPI_Datatype)datatype, dest, tag, (MPI_Comm)comm, real_status);
if (status != NULL)
{
status->MPI_SOURCE = real_status->MPI_SOURCE;
status->MPI_TAG = real_status->MPI_TAG;
status->MPI_ERROR = real_status->MPI_ERROR;
}
return res;
}
int MPI_Get_count(my_MPI_Status *status, my_MPI_Datatype datatype, int *count)
{
MPI_Status *real_status = (status != NULL) ? (MPI_Status*)&status->_original : MPI_STATUS_IGNORE;
return PMPI_Get_count(real_status, (MPI_Datatype)datatype, count);
}
在您的代码中:
#include "mympi.h"
...
MPI_Status status;
int count;
MPI_Recv(buf, 100, MPI_INT, 0, 10, MPI_COMM_WORLD, &status);
MPI_Get_count(&status, MPI_INT, &count);
...
然后,您的构建系统应检查实际 MPI 实现的 sizeof(MPI_Status) 是否小于或等于 MY_MAX_STATUS_SIZE。
以上只是一个快速而肮脏的想法——还没有测试它,一些 const 或转换可能在这里或那里丢失。它应该在实践中工作并且非常易于维护。
关于c++ - 如何为动态加载编写 MPI 包装器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38442254/
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
我刚刚为fedora安装了emacs。我想用emacs编写ruby。为ruby提供代码提示、代码完成类型功能所需的工具、扩展是什么? 最佳答案 ruby-mode已经包含在Emacs23之后的版本中。不过,它也可以通过ELPA获得。您可能感兴趣的其他一些事情是集成RVM、feature-mode(Cucumber)、rspec-mode、ruby-electric、inf-ruby、rinari(用于Rails)等。这是我当前用于Ruby开发的Emacs配置:https://github.com/citizen428/emacs
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co
只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您
如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:
我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty