| 1021 | } |
| 1022 | |
| 1023 | void FunctionLibraryRuntimeImpl::RunRemote(const Options& opts, Handle handle, |
| 1024 | gtl::ArraySlice<Tensor> args, |
| 1025 | std::vector<Tensor>* rets, |
| 1026 | Item* item, DoneCallback done) { |
| 1027 | string target_device = parent_->GetDeviceName(handle); |
| 1028 | string source_device = opts.source_device; |
| 1029 | Rendezvous* rendezvous = opts.rendezvous; |
| 1030 | DeviceContext* device_context; |
| 1031 | Status s = parent_->GetDeviceContext(target_device, &device_context); |
| 1032 | if (!s.ok()) { |
| 1033 | done(s); |
| 1034 | return; |
| 1035 | } |
| 1036 | int64 src_incarnation, target_incarnation; |
| 1037 | s = parent_->GetDeviceIncarnation(source_device, &src_incarnation); |
| 1038 | s.Update(parent_->GetDeviceIncarnation(target_device, &target_incarnation)); |
| 1039 | if (!s.ok()) { |
| 1040 | done(s); |
| 1041 | return; |
| 1042 | } |
| 1043 | |
| 1044 | const FunctionBody* fbody = GetFunctionBody(handle); |
| 1045 | FunctionCallFrame* frame = |
| 1046 | new FunctionCallFrame(fbody->arg_types, fbody->ret_types); |
| 1047 | Executor::Args* exec_args = new Executor::Args; |
| 1048 | ExecutorArgsFromOptions(opts, frame, exec_args); |
| 1049 | |
| 1050 | std::vector<AllocatorAttributes> args_alloc_attrs, rets_alloc_attrs; |
| 1051 | args_alloc_attrs.reserve(fbody->arg_types.size()); |
| 1052 | rets_alloc_attrs.reserve(fbody->ret_types.size()); |
| 1053 | // Note: Functions assume that int32's are always on host memory. |
| 1054 | for (const auto& arg_type : fbody->arg_types) { |
| 1055 | AllocatorAttributes arg_alloc_attrs; |
| 1056 | if (MTypeFromDType(arg_type) == HOST_MEMORY) { |
| 1057 | arg_alloc_attrs.set_on_host(true); |
| 1058 | } |
| 1059 | args_alloc_attrs.push_back(arg_alloc_attrs); |
| 1060 | } |
| 1061 | for (const auto& ret_type : fbody->ret_types) { |
| 1062 | AllocatorAttributes ret_alloc_attrs; |
| 1063 | if (MTypeFromDType(ret_type) == HOST_MEMORY) { |
| 1064 | ret_alloc_attrs.set_on_host(true); |
| 1065 | } |
| 1066 | rets_alloc_attrs.push_back(ret_alloc_attrs); |
| 1067 | } |
| 1068 | |
| 1069 | bool allow_dead_tensors = opts.allow_dead_tensors; |
| 1070 | |
| 1071 | // The ProcFLR sends the arguments to the function from the source_device to |
| 1072 | // the target_device. So here we receive those arguments. Similarly, when the |
| 1073 | // computation is done and stored in *rets, we send the return values back |
| 1074 | // to the source_device (caller) so that the ProcFLR can receive them later. |
| 1075 | std::vector<Tensor>* remote_args = new std::vector<Tensor>; |
| 1076 | ProcessFunctionLibraryRuntime::ReceiveTensorsAsync( |
| 1077 | source_device, target_device, "arg_", src_incarnation, args.size(), |
| 1078 | device_context, args_alloc_attrs, rendezvous, remote_args, |
| 1079 | [frame, remote_args, item, source_device, target_device, |
| 1080 | target_incarnation, rendezvous, device_context, rets, done, exec_args, |
nothing calls this directly
no test coverage detected