To demonstrate that it's the pipeline itself that's generating the subshell, and that curly braces won't change this either way:
#!/bin/bash
echo "Base: $BASHPID"
( echo "In (): $BASHPID" )   # This will differ from the base
{ echo "In {}: $BASHPID"; }  # This will match the base
# In bash, these will both differ from the base
echo "Pipeline, default config:"
{ echo " X: $BASHPID" >&2; } | { echo " Y: $BASHPID" >&2; }
# This is exactly the same without the {}s
echo "Pipeline, no {}s, default config:"
echo " X: $BASHPID" >&2 | echo " Y: $BASHPID" >&2
# Only the former will differ from the base if running a new enough bash
shopt -s lastpipe
echo "Pipeline, lastpipe enabled:"
{ echo " X: $BASHPID" >&2; } | { echo " Y: $BASHPID" >&2; }
Running this locally with bash 4.3, I get:
Base: 82811
In (): 82812
In {}: 82811
Pipeline, default config:
 X: 82813
 Y: 82814
Pipeline, no {}s, default config:
 X: 82815
 Y: 82816
Pipeline, lastpipe enabled:
 Y: 82811
 X: 82817
Note that since all pipeline components run simultaneously, there's no defined ordering of which of X or Y will emit output first; however, with lastpipe enabled, the last pipeline component is invoked in a shell that's already up and running (doesn't need to be fork()ed off from the main process), which slightly modifies the likelihood of who writes to stdout first.