DEV Community

Cover image for What Happens When You Let Your Code Cook in Parallel
astrospkc
astrospkc

Posted on

What Happens When You Let Your Code Cook in Parallel

#go

2 simple experimental examples to show how using go differs the whole thing and make it fast.

`package main

import (
"fmt"

"time"
)

func cook(dish string, seconds int) {

fmt.Printf("[%s] started cooking\n", dish)
time.Sleep(time.Duration(seconds) * time.Second)
fmt.Printf("[%s] done!\n", dish)
}

func main() {
start := time.Now()
cook("1080p", 3)
cook("720p", 2)
cook("480p", 1)

fmt.Printf("\n⏱ Total time: %v\n", time.Since(start).Round(time.Second))
fmt.Println("If ~3s → concurrent | If ~6s → sequential ")
}

// The output for this code :

[1080p] started cooking
[1080p] done!
[720p] started cooking
[720p] done!
[480p] started cooking
[480p] done!

⏱ Total time: 6s
If ~3s → concurrent | If ~6s → sequential`

This above code is running sequentially.

Write on Medium
Now, lets see the optimized way of doing it using go:

`package main

import (
"fmt"
"sync"
"time"
)

func cook(dish string, seconds int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("[%s] started cooking\n", dish)
time.Sleep(time.Duration(seconds) * time.Second)
fmt.Printf("[%s] done!\n", dish)
}

func main() {
var wg sync.WaitGroup

start := time.Now()
wg.Add(3)
go cook("1080p", 3, &wg)
go cook("720p", 2, &wg)
go cook("480p", 1, &wg)
wg.Wait()

fmt.Printf("\n⏱ Total time: %v\n", time.Since(start).Round(time.Second))
fmt.Println("If ~3s → concurrent | If ~6s → sequential ")
}

//output:

[480p] started cooking
[1080p] started cooking
[720p] started cooking
[480p] done!
[720p] done!
[1080p] done!

⏱ Total time: 3s
If ~3s → concurrent | If ~6s → sequential`

So now with this simple example we came to know how to use go and when to use go.

Now this above technique , I have used in transcoding video:

`func HLSTranscode(videoUploadId int64, inputKey string, userId int64) error {

// check if already exists
exists, err := connect.Db.NewSelect().
    Model((*models.VideoQuality)(nil)).
    Where("video_upload_id = ?", videoUploadId).
    Exists(context.Background())
if err != nil {
    return fmt.Errorf("failed to check if video exists: %w", err)
}
if exists {
    return nil
}

// download from S3
inputFile, err := DownloadFromS3(inputKey)
if err != nil {
    return fmt.Errorf("error while downloading from s3: %w", err)
}
defer os.Remove(inputFile)

var qualities = []Quality{
    {Name: "1080p", Resolution: "1920x1080", Bitrate: "4000k", AudioRate: "192k"},
    {Name: "720p",  Resolution: "1280x720",  Bitrate: "2500k", AudioRate: "128k"},
    {Name: "480p",  Resolution: "854x480",   Bitrate: "1000k", AudioRate: "96k"},
}

//  semaphore — max 3 qualities running in parallel
semaphore := make(chan struct{}, 3)

//  waitgroup — wait for ALL qualities to finish
var wg sync.WaitGroup

//  collect errors from goroutines
errChan := make(chan error, len(qualities))

for _, q := range qualities {
    wg.Add(1)

    // capture loop variable — critical in Go
    q := q

    go func() {
        defer wg.Done()

        // acquire semaphore slot
        semaphore <- struct{}{}
        defer func() { <-semaphore }() // release when done

        fmt.Printf("started transcoding: %s\n", q.Name)

        err := transcodeQuality(inputFile, videoUploadId, userId, q)
        if err != nil {
            fmt.Printf("failed transcoding %s: %v\n", q.Name, err)
            errChan <- fmt.Errorf("quality %s failed: %w", q.Name, err)
            return
        }

        fmt.Printf("✅ finished transcoding: %s\n", q.Name)
    }()
}

// wait for all goroutines to finish
wg.Wait()
close(errChan)

// collect any errors
var errs []string
for err := range errChan {
    if err != nil {
        errs = append(errs, err.Error())
    }
}
if len(errs) > 0 {
    return fmt.Errorf("transcoding errors: %s", strings.Join(errs, ", "))
}

fmt.Println(" all qualities transcoded successfully")
return nil
Enter fullscreen mode Exit fullscreen mode

}`

Here the role of Semaphore is : Limit the number the goroutines running concurrently.

Follow in github:
https://github.com/astrospkc

Top comments (0)