What is the difference between parallel activities in temporal go routines vs just storing the futures

Hey! I was looking into running activities in parallel to speed up the execution of my workflow and then wait to get all the results of the activities and I came across two ways of doing it.
Say I have this activity:

func () DoSth () error {
	// sth that might take time

Way 1:

futures := make([]workflow.Future, 0, 1000)
for index := 0; index < 1000; index++ {
	future := workflow.ExecuteActivity(ctx, DoSth)
	futures = append(futures, future)

for _, f := range futures {
        var res int
        err := f.Get(ctx, &res)

Way 2 (gotten from: samples-go/splitmerge_workflow.go at main · temporalio/samples-go · GitHub):

for i := 1; i <= workerCount; i++ {
		chunkID := i
		workflow.Go(ctx, func(ctx workflow.Context) {
			var result ChunkResult
			err := workflow.ExecuteActivity(ctx, ChunkProcessingActivity, chunkID).Get(ctx, &result)
			if err == nil {
				chunkResultChannel.Send(ctx, result)
			} else {
				chunkResultChannel.Send(ctx, err)

	var totalItemCount, totalSum int
	for i := 1; i <= workerCount; i++ {
		var v interface{}
		chunkResultChannel.Receive(ctx, &v)
		switch r := v.(type) {
		case error:
		// failed to process this chunk
		// some proper error handling code here
		case ChunkResult:
			totalItemCount += r.NumberOfItemsInChunk
			totalSum += r.SumInChunk

So, what’s the difference between these two ways of executing the activities in parallel and how do I know which to use?

There is no practical difference in performance. Only one coroutine runs at a time in a workflow anyways and they will translate to the same events. Use which way feels best for you (I’d say “Way 1”).