I typically advocate that if you can do something in Salesforce in a declarative way, you probably should.
However, I think your situation is different because of the sheer volume you expect. In addition to (potentially) more robust bulkification, you will be able to achieve much better error handling through
Apex. Errors are fairly opaque via
Process Builder last I checked. In addition to improving message clarity, this will allow you to support partial success operations, instead of making them all or none in the case of a failure.
A fairly common pattern for mapping errors in this case would look something like:
catch (DmlException dmx)
Map<Id, List<Opportunity>> accountToOpportunities = new Map<Id, List<Opportunity>>();
for (Opportunity opportunity : opportunities)
accountToOpportunities.put(opportunity.AccountId, new List<Opportunity>();
for (Integer i = 0; i < dmx.getNumDml(); i++)
Account relatedAccount = relatedAccounts[dmx.getDmlIndex(i)];
for (Opportunity opportunity : accountToOpportunities.get(relatedAccount.Id))