How to control the size or time of a purely computational task?

Go garbage collector hung due to preemptive scheduling failure

go build the following code and use the command ‘GODEBUG=gctrace=2 GOGC=5 ./main’ to run.

package main                                                                                                                                                                                              
                                                                                                                                                                                                          
import (                                                                                                                                                                                                  
    "fmt"                                                                                                                                                                                                 
    "time"                                                                                                                                                                                                
)                                                                                                                                                                                                         
                                                                                                                                                                                                          
const (                                                                                                                                                                                                   
    Num = 1 << 20                                                                                                                                                                                         
)                                                                                                                                                                                                         
                                                                                                                                                                                                          
func main() {                                                                                                                                                                                             
    go func() {                                                                                                                                                                                           
        for {                                                                                                                                                                                             
            select {                                                                                                                                                                                      
            case <-time.After(time.Second):                                                                                                                                                               
                fmt.Printf("hello world\n")                                                                                                                                                               
            }                                                                                                                                                                                             
        }                                                                                                                                                                                                 
    }()                                                                                                                                                                                                   
    vs := make([]int64, Num)                                                                                                                                                                              
    for i := 0; i < 100; i++ {                                                                                                                                                                            
        for i := 0; i < len(vs); i++ {                                                                                                                                                                    
            vs = append(vs[:i], vs[i+1:]...)                                                                                                                                                              
            i--                                                                                                                                                                                           
        }                                                                                                                                                                                                 
        vs = vs[:Num]                                                                                                                                                                                     
    }                                                                                                                                                                                                     
}                      

For a purely computational loop, how to set a more reasonable value in the design of the program? 8192 is a reasonable value?

1 Like

I’m not sure what you’re asking. Are you asking how to timeout after executing for too long? How do you define “more reasonable?”

I think so, for a purely computational task, one needs to determine a more reasonable period that he should execute, otherwise if the task blocks the gc it will lead to oom, the problem I’m having is actually something like this task leading to oom. Because this will happen.

  1. gc on
  2. gc waiting for loop to end
  3. the service receives a new request and processes the request (this process will allocate memory)
  4. oom

My main problem is that I don’t know how to better write such code so that gc can work well, I don’t want to add something to the loop so that gc can terminate this loop, which will lead to performance degradation, what I want is to make this loop as small as possible (but not small enough will lead to performance degradation), such as 8192, but this value I’m not sure how much is appropriate.

I think it’s better to try to use control over the GC than polluting your code with hacks to try to work around it. SetGCPercent and SetMaxHeap might help. If that fails, try asking on the golang nuts mailing list? Ian Lance Taylor and others familiar with the GC implementation answer questions there.

I’m not sure that you have any alternatives other than to put a call to runtime.Gosched() in there. Your example doesn’t actually produce any garbage because it preallocates the slice and only reslices it to smaller sizes. I added another goroutine that kept appending to a slice* and crashed w/ an out of memory error. If I add a call to runtime.Gosched(), then the other goroutines, including the garbage collector(s), were able to run.

It is my understanding that goroutines that produce a lot of garbage are supposed to stop and dedicate time to cleanup some of that garbage. I’m not sure why that doesn’t happen, but I haven’t had the opportunity to read up on it.

* The Go Play Space

thank you

The reason this happened is this, in my project (a database project), then a transaction executed a very large number of inserts (then some of these insert data will be swiped to s3, then some of these inserts become nil), then commit time wrote a loop to shrink the transaction size (remove these nil), and then This loop caused some problems with gc. My current fix is to remove this loop directly

OK, well with that information, I suggest you change your algorithm for removing nils. Using append like this is great for removing a single range of elements from a slice, but if you need to scan through the slice and remove multiple discontiguous ranges of elements, the time spent moving the slice’s elements over and over again grows asymptotically with the number of elements. If you instead do something more like this:

// removeZeros removes elements from the slice that are equal to their
// zero value (e.g. removes 0 for ints, nil for interface{}s, etc.).
func removeZeros[T comparable](vs []T) []T {
	var zero T
	delta := 0
	for i := 0; i < len(vs); i++ {
		if vs[i] == zero {
			delta--
			continue
		}
		vs[i+delta] = vs[i]
	}
	return vs[:len(vs)+delta]
}

I presume your loop looks something more like this in your real code:

        for i := 0; i < len(inserts); i++ {
            if inserts[i] == nil {
                inserts = append(inserts[:i], inserts[i+1:]...)
                i--
            }
        }

You would change that whole loop to just:

inserts = removeZeros(inserts)

thank you

I think this talk from gopher con could be quite relevant:

While this particular problem can be solved by optimization, it is still good to know how a tight loop can be structured to help garbage-collection.

Apologies for the confusion. I assumed you were referring to the performance of your website when you mentioned “more reasonable.” However, if you were asking about setting a timeout for code execution, I can provide clarification.

Setting a timeout for code execution allows you to define a maximum duration for a certain operation or task. If the code execution exceeds that time limit, you can take appropriate action, such as terminating the execution or displaying an error message. The specific timeout duration depends on your requirements and the nature of the task being performed.

To implement a timeout in your code, you can use language-specific features or constructs. Here’s a general approach using pseudo-code as an example:

start timer

while timer has not reached timeout duration:
execute code

if code execution completes:
    break the loop

if timer has reached timeout duration:
    handle timeout (e.g., terminate execution, display an error message)

In this example, you start a timer before executing the code. Inside a loop, you continuously check if the timer has reached the timeout duration. If the code execution completes before the timeout, you break out of the loop. However, if the timer reaches the timeout duration, you handle the timeout accordingly.

same issue on my side too.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.