-
Notifications
You must be signed in to change notification settings - Fork 4.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a generic mechanism to specify compute accelerators to use in the configuration #36699
Changes from all commits
7a1bbcf
d800249
010f6a3
417bdca
6ccf361
023bf0e
fc821ee
5caf7b8
e0d39f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import FWCore.ParameterSet.Config as cms | ||
|
||
# This fragment is intended to collect all ProcessAccelerator objects | ||
# used in production | ||
|
||
from HeterogeneousCore.CUDACore.ProcessAcceleratorCUDA_cfi import ProcessAcceleratorCUDA |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#include "FWCore/ParameterSet/interface/ParameterSet.h" | ||
|
||
namespace edm { | ||
void ensureAvailableAccelerators(edm::ParameterSet const& parameterSet); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#include "FWCore/Framework/interface/ensureAvailableAccelerators.h" | ||
#include "FWCore/Utilities/interface/EDMException.h" | ||
|
||
#include <algorithm> | ||
#include <vector> | ||
|
||
namespace edm { | ||
void ensureAvailableAccelerators(edm::ParameterSet const& parameterSet) { | ||
auto const& selectedAccelerators = | ||
parameterSet.getUntrackedParameter<std::vector<std::string>>("@selected_accelerators"); | ||
ParameterSet const& optionsPset(parameterSet.getUntrackedParameterSet("options")); | ||
if (selectedAccelerators.empty()) { | ||
Exception ex(errors::UnavailableAccelerator); | ||
ex << "The system has no compute accelerators that match the patterns specified in " | ||
"process.options.accelerators:\n"; | ||
auto const& patterns = optionsPset.getUntrackedParameter<std::vector<std::string>>("accelerators"); | ||
for (auto const& pat : patterns) { | ||
ex << " " << pat << "\n"; | ||
} | ||
ex << "\nThe following compute accelerators are available:\n"; | ||
auto const& availableAccelerators = | ||
parameterSet.getUntrackedParameter<std::vector<std::string>>("@available_accelerators"); | ||
for (auto const& acc : availableAccelerators) { | ||
ex << " " << acc << "\n"; | ||
} | ||
|
||
throw ex; | ||
} | ||
} | ||
} // namespace edm |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
#define CATCH_CONFIG_MAIN | ||
#include "catch.hpp" | ||
|
||
#include "DataFormats/TestObjects/interface/ToyProducts.h" | ||
#include "FWCore/ParameterSet/interface/ParameterSet.h" | ||
#include "FWCore/ParameterSetReader/interface/ParameterSetReader.h" | ||
#include "FWCore/TestProcessor/interface/TestProcessor.h" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Glad to see TestProcessor being used. Out of curiosity, what drove you to use TestProcessor as apposed to a shell script running cmsRun? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I started with a similar |
||
|
||
#include <fmt/format.h> | ||
|
||
#include <iostream> | ||
#include <string_view> | ||
|
||
static constexpr auto s_tag = "[ProcessAccelerator]"; | ||
|
||
namespace { | ||
std::string makeConfig(bool test2Enabled, | ||
std::string_view test1, | ||
std::string_view test2, | ||
std::string_view accelerator) { | ||
const std::string appendTest2 = test2Enabled ? "self._enabled.append('test2')" : ""; | ||
return fmt::format( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice use of fmt::format! |
||
R"_(from FWCore.TestProcessor.TestProcess import * | ||
import FWCore.ParameterSet.Config as cms | ||
class ProcessAcceleratorTest(cms.ProcessAccelerator): | ||
def __init__(self): | ||
super(ProcessAcceleratorTest,self).__init__() | ||
self._labels = ["test1", "test2"] | ||
self._enabled = ["test1"] | ||
{} | ||
def labels(self): | ||
return self._labels | ||
def enabledLabels(self): | ||
return self._enabled | ||
class SwitchProducerTest(cms.SwitchProducer): | ||
def __init__(self, **kargs): | ||
super(SwitchProducerTest,self).__init__( | ||
dict( | ||
cpu = cms.SwitchProducer.getCpu(), | ||
test1 = lambda accelerators: ("test1" in accelerators, 2), | ||
test2 = lambda accelerators: ("test2" in accelerators, 3), | ||
), **kargs) | ||
process = TestProcess() | ||
process.options.accelerators = ["{}"] | ||
process.ProcessAcceleratorTest = ProcessAcceleratorTest() | ||
process.s = SwitchProducerTest( | ||
cpu = cms.EDProducer('IntProducer', ivalue = cms.int32(0)), | ||
test1 = {}, | ||
test2 = {} | ||
) | ||
process.moduleToTest(process.s) | ||
)_", | ||
appendTest2, | ||
accelerator, | ||
test1, | ||
test2); | ||
} | ||
} // namespace | ||
|
||
TEST_CASE("Configuration", s_tag) { | ||
const std::string test1{"cms.EDProducer('IntProducer', ivalue = cms.int32(1))"}; | ||
const std::string test2{"cms.EDProducer('ManyIntProducer', ivalue = cms.int32(2), values = cms.VPSet())"}; | ||
|
||
const std::string baseConfig_auto = makeConfig(true, test1, test2, "*"); | ||
const std::string baseConfig_test1 = makeConfig(true, test1, test2, "test1"); | ||
const std::string baseConfig_test2 = makeConfig(true, test1, test2, "test2"); | ||
const std::string baseConfigTest2Disabled_auto = makeConfig(false, test1, test2, "*"); | ||
const std::string baseConfigTest2Disabled_test1 = makeConfig(false, test1, test2, "test1"); | ||
const std::string baseConfigTest2Disabled_test2 = makeConfig(false, test1, test2, "test2"); | ||
|
||
SECTION("Configuration hash is not changed") { | ||
auto pset_auto = edm::readConfig(baseConfig_auto); | ||
auto pset_test1 = edm::readConfig(baseConfig_test1); | ||
auto pset_test2 = edm::readConfig(baseConfig_test2); | ||
auto psetTest2Disabled_auto = edm::readConfig(baseConfigTest2Disabled_auto); | ||
auto psetTest2Disabled_test1 = edm::readConfig(baseConfigTest2Disabled_test1); | ||
auto psetTest2Disabled_test2 = edm::readConfig(baseConfigTest2Disabled_test2); | ||
pset_auto->registerIt(); | ||
pset_test1->registerIt(); | ||
pset_test2->registerIt(); | ||
psetTest2Disabled_auto->registerIt(); | ||
psetTest2Disabled_test1->registerIt(); | ||
psetTest2Disabled_test2->registerIt(); | ||
REQUIRE(pset_auto->id() == pset_test1->id()); | ||
REQUIRE(pset_auto->id() == pset_test2->id()); | ||
REQUIRE(pset_auto->id() == psetTest2Disabled_auto->id()); | ||
REQUIRE(pset_auto->id() == psetTest2Disabled_test1->id()); | ||
REQUIRE(pset_auto->id() == psetTest2Disabled_test2->id()); | ||
} | ||
|
||
edm::test::TestProcessor::Config config_auto{baseConfig_auto}; | ||
edm::test::TestProcessor::Config config_test1{baseConfig_test1}; | ||
edm::test::TestProcessor::Config config_test2{baseConfig_test2}; | ||
edm::test::TestProcessor::Config configTest2Disabled_auto{baseConfigTest2Disabled_auto}; | ||
edm::test::TestProcessor::Config configTest2Disabled_test1{baseConfigTest2Disabled_test1}; | ||
edm::test::TestProcessor::Config configTest2Disabled_test2{baseConfigTest2Disabled_test2}; | ||
|
||
SECTION("Base configuration is OK") { REQUIRE_NOTHROW(edm::test::TestProcessor(config_auto)); } | ||
|
||
SECTION("No event data") { | ||
edm::test::TestProcessor tester(config_auto); | ||
REQUIRE_NOTHROW(tester.test()); | ||
} | ||
|
||
SECTION("beginJob and endJob only") { | ||
edm::test::TestProcessor tester(config_auto); | ||
REQUIRE_NOTHROW(tester.testBeginAndEndJobOnly()); | ||
} | ||
|
||
SECTION("Run with no LuminosityBlocks") { | ||
edm::test::TestProcessor tester(config_auto); | ||
REQUIRE_NOTHROW(tester.testRunWithNoLuminosityBlocks()); | ||
} | ||
|
||
SECTION("LuminosityBlock with no Events") { | ||
edm::test::TestProcessor tester(config_auto); | ||
REQUIRE_NOTHROW(tester.testLuminosityBlockWithNoEvents()); | ||
} | ||
|
||
SECTION("Test2 enabled, acclerators=*") { | ||
edm::test::TestProcessor tester(config_auto); | ||
auto event = tester.test(); | ||
REQUIRE(event.get<edmtest::IntProduct>()->value == 2); | ||
} | ||
|
||
SECTION("Test2 enabled, acclerators=test1") { | ||
edm::test::TestProcessor tester(config_test1); | ||
auto event = tester.test(); | ||
REQUIRE(event.get<edmtest::IntProduct>()->value == 1); | ||
} | ||
|
||
SECTION("Test2 enabled, acclerators=test2") { | ||
edm::test::TestProcessor tester(config_test2); | ||
auto event = tester.test(); | ||
REQUIRE(event.get<edmtest::IntProduct>()->value == 2); | ||
} | ||
|
||
SECTION("Test2 disabled, accelerators=*") { | ||
edm::test::TestProcessor tester(configTest2Disabled_auto); | ||
auto event = tester.test(); | ||
REQUIRE(event.get<edmtest::IntProduct>()->value == 1); | ||
} | ||
|
||
SECTION("Test2 disabled, accelerators=test1") { | ||
edm::test::TestProcessor tester(configTest2Disabled_test1); | ||
auto event = tester.test(); | ||
REQUIRE(event.get<edmtest::IntProduct>()->value == 1); | ||
} | ||
|
||
SECTION("Test2 disabled, accelerators=test2") { | ||
REQUIRE_THROWS_WITH( | ||
edm::test::TestProcessor(configTest2Disabled_test2), | ||
Catch::Contains("The system has no compute accelerators that match the patterns") && Catch::Contains("test1")); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#!/bin/bash | ||
|
||
test=testProcessAccelerator | ||
LOCAL_TEST_DIR=${CMSSW_BASE}/src/FWCore/Integration/test | ||
LOCAL_TMP_DIR=${CMSSW_BASE}/tmp/${SCRAM_ARCH} | ||
|
||
function die { echo Failure $1: status $2 ; exit $2 ; } | ||
|
||
pushd ${LOCAL_TMP_DIR} | ||
|
||
echo "*************************************************" | ||
echo "accelerators=*" | ||
cmsRun ${LOCAL_TEST_DIR}/${test}_cfg.py || die "cmsRun ${test}_cfg.py" $? | ||
|
||
echo "*************************************************" | ||
echo "accelerators=*, enableTest2" | ||
cmsRun ${LOCAL_TEST_DIR}/${test}_cfg.py -- --enableTest2 || die "cmsRun ${test}_cfg.py -- --enableTest2" $? | ||
|
||
echo "*************************************************" | ||
echo "accelerators=test1" | ||
cmsRun ${LOCAL_TEST_DIR}/${test}_cfg.py -- --accelerators=test1 || die "cmsRun ${test}_cfg.py -- --accelerators=test1" $? | ||
|
||
echo "*************************************************" | ||
echo "accelerators=test2" | ||
cmsRun -j testProcessAccelerators_jobreport.xml ${LOCAL_TEST_DIR}/${test}_cfg.py -- --accelerators=test2 && die "cmsRun ${test}_cfg.py -- --accelerators=test2 did not fail" 1 | ||
EXIT_CODE=$(edmFjrDump --exitCode testProcessAccelerators_jobreport.xml) | ||
if [ "x${EXIT_CODE}" != "x8035" ]; then | ||
echo "ProcessAccelerator test for unavailable accelerator reported exit code ${EXIT_CODE} which is different from the expected 8035" | ||
exit 1 | ||
fi | ||
|
||
echo "*************************************************" | ||
echo "accelerators=test1, enableTest2" | ||
cmsRun ${LOCAL_TEST_DIR}/${test}_cfg.py -- --accelerators=test1 --enableTest2 || die "cmsRun ${test}_cfg.py -- --accelerators=test1 --enableTest2" $? | ||
|
||
echo "*************************************************" | ||
echo "accelerators=test2, enableTest2" | ||
cmsRun ${LOCAL_TEST_DIR}/${test}_cfg.py -- --accelerators=test2 --enableTest2 || die "cmsRun ${test}_cfg.py -- --accelerators=test2 --enableTest2" $? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import FWCore.ParameterSet.Config as cms | ||
|
||
import argparse | ||
import sys | ||
|
||
parser = argparse.ArgumentParser(prog=sys.argv[0], description='Test ProcessAccelerator.') | ||
|
||
parser.add_argument("--enableTest2", help="Enable test2 accelerator", action="store_true") | ||
parser.add_argument("--accelerators", type=str, help="Comma-separated string for accelerators to enable") | ||
|
||
argv = sys.argv[:] | ||
if '--' in argv: | ||
argv.remove("--") | ||
args, unknown = parser.parse_known_args(argv) | ||
|
||
class ProcessAcceleratorTest(cms.ProcessAccelerator): | ||
def __init__(self): | ||
super(ProcessAcceleratorTest,self).__init__() | ||
self._labels = ["test1", "test2"] | ||
self._enabled = ["test1"] | ||
if args.enableTest2: | ||
self._enabled.append("test2") | ||
def labels(self): | ||
return self._labels | ||
def enabledLabels(self): | ||
return self._enabled | ||
|
||
class SwitchProducerTest(cms.SwitchProducer): | ||
def __init__(self, **kargs): | ||
super(SwitchProducerTest,self).__init__( | ||
dict( | ||
cpu = cms.SwitchProducer.getCpu(), | ||
test1 = lambda accelerators: ("test1" in accelerators, 2), | ||
test2 = lambda accelerators: ("test2" in accelerators, 3), | ||
), **kargs) | ||
|
||
process = cms.Process("PROD1") | ||
|
||
process.add_(ProcessAcceleratorTest()) | ||
|
||
process.source = cms.Source("EmptySource") | ||
process.maxEvents.input = 3 | ||
if args.accelerators is not None: | ||
process.options.accelerators = args.accelerators.split(",") | ||
|
||
process.intProducer1 = cms.EDProducer("ManyIntProducer", ivalue = cms.int32(1)) | ||
process.intProducer2 = cms.EDProducer("ManyIntProducer", ivalue = cms.int32(2)) | ||
process.failIntProducer = cms.EDProducer("ManyIntProducer", ivalue = cms.int32(-1), throw = cms.untracked.bool(True)) | ||
|
||
if args.enableTest2 and ("test2" in process.options.accelerators or "*" in process.options.accelerators): | ||
process.intProducer1.throw = cms.untracked.bool(True) | ||
else: | ||
process.intProducer2.throw = cms.untracked.bool(True) | ||
|
||
process.intProducer = SwitchProducerTest( | ||
cpu = cms.EDProducer("AddIntsProducer", labels = cms.VInputTag("failIntProducer")), | ||
test1 = cms.EDProducer("AddIntsProducer", labels = cms.VInputTag("intProducer1")), | ||
test2 = cms.EDProducer("AddIntsProducer", labels = cms.VInputTag("intProducer2")) | ||
) | ||
|
||
process.intConsumer = cms.EDProducer("AddIntsProducer", labels = cms.VInputTag("intProducer")) | ||
|
||
process.t = cms.Task( | ||
process.failIntProducer, | ||
process.intProducer1, | ||
process.intProducer2, | ||
process.intProducer, | ||
) | ||
process.p = cms.Path( | ||
process.intConsumer, | ||
process.t | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also for a follow-up PR, this could be moved inside the
if
block