As validation happens at a location (e.g. in a validator object) where no access is present to whatever logic which called the validation, the only way to abort the action of the caller is to throw an exception. Also, a save operation's outcome if something is wrong can only be total abortion of the action, so an exception thrown is the least of the problems, and the little overhead of an exception is really not noticed when you compare it with rolling back the transaction. The exception makes it easy to cancel the operation, as a lot can happen during the save operation, and the only solid way to make the save operation abort is by throwing an exception.
While an exception is not as fast as a normal if statement, one shouldn't exaggerate the slowness of it, it's not as if you'll notice it if an exception is thrown. In a loop, sure, but that's not what's happening here.
If you want to validate a field's value before it's set (the right place to do so), you don't need exceptions. The built-in validation pipeline calls into OnValidateFieldValue, which calls into an existing Validator object in the entity, if present, otherwise it simply returns true. You can override this method if you want, in a partial class of the entity.
This method simply returns true or false. If false, the field isn't set with the value. You can set the error message on the field using the SetEntityFieldError method.
Aren't you mixing two things in your post? Namely field set validation and save validation?