This is a complex question with a lot of small details that really matter, the performance will vary based on platform and application. So you should profile for possible bottlenecks before investing in optimizations.
That said, firstly, I assume you should reduce uploads and updates as much as you can, for example use instancing.
Secondly, note that GPUs can’t transfer buffers and render at the same time, so all OpenGL commands in the command queue are processed sequentially by the device. There are different ways to copy data and/or make it avaialbe to be used by the GPU.
There are various ways to stream data to GPU
glBufferSubData is like memcpy. you pass a pointer and DMA operation might be performed, I said might because memory might be pinned in CPU memory and used directly by the GPU without actually a memory transfer to GPU happening, depending on the usage flag (GL_STREAM). In opinion you should try this at first because it's simpler to implement.
2- getting a pointer to internal memory using
If the above isn't good enough you can use
glMapBuffer, you get a pointer to internal memory, and you can use this pointer to full the buffer directly, this is good with file read and write operations, as you can directly map the file data to GPU memory rather than copying to a temp buffer first. If you don't want to map the whole buffer you can use
glMapBufferRange which can be used to map a portion of the buffer.
One trick is to create a large buffer, use the first half for rendering and the second half for updating.
3- Buffer Orphaning
Regarding buffer orphaning this can be done using glBufferData with null and the same parameters it had. The driver will return the memory block once it's not used. And will be used by the next glBufferData call (no new memory will be allocated).
All the methods mentioned cause alot of expensive sync, again GPUs can’t transfer buffers and render at the same time.
The fastest (and hardest to get right) method is to use buffers without synchronization you can use
GL_MAP_UNSYNCHRONIZED_BIT flag with
glMapBufferRange, the problem is that no sync is done, so we might upload data to a buffer begin used, and hence screwing everything up. You can use multiple buffers with unsync bit to make things a little bit easier.