Skip to content

Commit

Permalink
Renamed load-limit to memory-limit
Browse files Browse the repository at this point in the history
Improved so that memory-limit can be specified overall.
  • Loading branch information
noborus committed May 14, 2023
1 parent da26180 commit 0b2e4ab
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 53 deletions.
42 changes: 25 additions & 17 deletions README.md
Expand Up @@ -533,49 +533,57 @@ You can change how much is written using `--exit-write-before` and `--exit-write

## 4. <a name='how-to-reduce-memory-usage'></a>How to reduce memory usage

Since **v0.20.0** it no longer loads everything into memory.
The first chunk from the beginning to the 10,000th line is loaded into memory
Since **v0.22.0** it no longer loads everything into memory.
The first chunk from the beginning to the 10,000th line is loaded into memory
and never freed.
Therefore, files with less than 10,000 lines do not change behavior.

### 4.1. <a name='regular-file-(seekable)'></a>Regular file (seekable)
The `--memory-limit` option can be used to limit the chunks loaded into memory.
Memory limits vary by file type.

Normally large (10,000+ lines) files are loaded in chunks when needed. It also frees chunks that are no longer needed.
The default limit for loading chunks is 100. To change this,
use the `--file-load-limit` option or specify `FileLoadChunkLimit` in config. The minimum you can specify is 2.
Also, go may use a lot of memory until the memory is freed by GC.
Also consider setting the environment variable `GOMEMLIMIT`.

```console
ov --file-load-limit 3 /var/log/syslog
export GOMEMLIMIT=100MiB
```

```yaml
FileLoadChunkLimit: 3
```
### 4.1. <a name='regular-file-(seekable)'></a>Regular file (seekable)

Also, go may use a lot of memory until the memory is freed by GC.
Also consider setting the environment variable `GOMEMLIMIT`.
Normally large (10,000+ lines) files are loaded in chunks when needed. It also frees chunks that are no longer needed.
If `--memory-limit` is not specified, it will be limited to 100.

```console
export GOMEMLIMIT=100MiB
ov --memory-limit-file 3 /var/log/syslog
```

Specify `MemoryLimit` in the configuration file.

```yaml
MemoryLimitFile: 3
```

### 4.2. <a name='other-files,-pipes(non-seekable)'></a>Other files, pipes(Non-seekable)

Non-seekable files and pipes cannot be read again, so they must exist in memory.

If you specify the upper limit of chunks with `--load-limit` or `LoadChunkLimit`,
If you specify the upper limit of chunks with `--memory-limit` or `MemoryLimit`,
it will read up to the upper limit first, but after that,
when the displayed position advances, the old chunks will be released.
The load-limit defaults to `-1`, which is unlimited. Minimum is `2`.
Unlimited if `--memory-limit` is not specified.

```console
cat /var/log/syslog | ov --load-limit 3
cat /var/log/syslog | ov --memory-limit 10
```

It is recommended to put a limit in the config file as you may receive output larger than memory.

```yaml
LoadChunkLimit: 3
MemoryLimit: 1000
```

You can also use the `--memory-limit-file` option and the `MemoryLimitFile` setting for those who think regular files are good memory saving.

## 5. <a name='command-option'></a>Command option

```console
Expand Down
20 changes: 6 additions & 14 deletions main.go
Expand Up @@ -80,16 +80,8 @@ It supports various compressed files(gzip, bzip2, zstd, lz4, and xz).
// Set a global variable to convert to a style before opening the file.
oviewer.OverStrikeStyle = oviewer.ToTcellStyle(config.StyleOverStrike)
oviewer.OverLineStyle = oviewer.ToTcellStyle(config.StyleOverLine)

if config.FileLoadChunksLimit >= 0 && config.FileLoadChunksLimit < 2 {
config.FileLoadChunksLimit = 2
}
oviewer.FileLoadChunksLimit = config.FileLoadChunksLimit
if config.LoadChunksLimit >= 0 && config.LoadChunksLimit < 2 {
config.LoadChunksLimit = 2
}
oviewer.LoadChunksLimit = config.LoadChunksLimit

oviewer.MemoryLimit = config.MemoryLimit
oviewer.MemoryLimitFile = config.MemoryLimitFile
SetRedirect()

if execCommand {
Expand Down Expand Up @@ -317,11 +309,11 @@ func init() {
rootCmd.PersistentFlags().StringP("view-mode", "", "", "view mode")
_ = viper.BindPFlag("ViewMode", rootCmd.PersistentFlags().Lookup("view-mode"))

rootCmd.PersistentFlags().IntP("load-limit", "", -1, "Limit loading chunks")
_ = viper.BindPFlag("LoadChunksLimit", rootCmd.PersistentFlags().Lookup("load-limit"))
rootCmd.PersistentFlags().IntP("memory-limit", "", -1, "Number of chunks to limit in memory")
_ = viper.BindPFlag("MemoryLimit", rootCmd.PersistentFlags().Lookup("memory-limit"))

rootCmd.PersistentFlags().IntP("file-load-limit", "", 100, "Limit chunks loading files into memory")
_ = viper.BindPFlag("FileLoadChunksLimit", rootCmd.PersistentFlags().Lookup("file-load-limit"))
rootCmd.PersistentFlags().IntP("memory-limit-file", "", 100, "The number of chunks to limit in memory for the file")
_ = viper.BindPFlag("MemoryLimitFile", rootCmd.PersistentFlags().Lookup("memory-limit-file"))

rootCmd.PersistentFlags().BoolP("debug", "", false, "debug mode")
_ = viper.BindPFlag("Debug", rootCmd.PersistentFlags().Lookup("debug"))
Expand Down
26 changes: 21 additions & 5 deletions oviewer/chunk.go
Expand Up @@ -11,10 +11,26 @@ import (
// setNewLoadChunks creates a new LRU cache.
// Manage chunks loaded in LRU cache.
func (m *Document) setNewLoadChunks() {
capacity := FileLoadChunksLimit + 1
mlFile := MemoryLimitFile
if MemoryLimit >= 0 {
if MemoryLimit < 2 {
MemoryLimit = 2
}
mlFile = MemoryLimit
MemoryLimitFile = MemoryLimit
}

if MemoryLimitFile > mlFile {
MemoryLimitFile = mlFile
}
if MemoryLimitFile < 2 {
MemoryLimitFile = 2
}

capacity := MemoryLimitFile + 1
if !m.seekable {
if LoadChunksLimit > 0 {
capacity = LoadChunksLimit + 1
if MemoryLimit > 0 {
capacity = MemoryLimit + 1
}
}

Expand Down Expand Up @@ -48,7 +64,7 @@ func (m *Document) evictChunksFile(chunkNum int) error {
if chunkNum == 0 {
return nil
}
for m.loadedChunks.Len() > FileLoadChunksLimit {
if m.loadedChunks.Len() >= MemoryLimitFile {
k, _, _ := m.loadedChunks.GetOldest()
if chunkNum != k {
m.unloadChunk(k)
Expand All @@ -68,7 +84,7 @@ func (m *Document) evictChunksMem(chunkNum int) {
if chunkNum == 0 {
return
}
if (LoadChunksLimit < 0) || (m.loadedChunks.Len() < LoadChunksLimit) {
if (MemoryLimit < 0) || (m.loadedChunks.Len() < MemoryLimit) {
return
}
k, _, _ := m.loadedChunks.GetOldest()
Expand Down
4 changes: 2 additions & 2 deletions oviewer/config.go
Expand Up @@ -3,8 +3,8 @@ package oviewer
// NewConfig return the structure of Config with default values.
func NewConfig() Config {
return Config{
LoadChunksLimit: -1,
FileLoadChunksLimit: 100,
MemoryLimit: -1,
MemoryLimitFile: 100,
StyleHeader: OVStyle{
Bold: true,
},
Expand Down
4 changes: 2 additions & 2 deletions oviewer/control.go
Expand Up @@ -123,8 +123,8 @@ func (m *Document) controlFile(sc controlSpecifier, reader *bufio.Reader) (*bufi
case requestStart:
return m.firstRead(reader)
case requestContinue:
if !m.seekable && LoadChunksLimit > 0 {
if m.loadedChunks.Len() >= LoadChunksLimit {
if !m.seekable {
if MemoryLimit > 0 && m.loadedChunks.Len() >= MemoryLimit {
// Stopped loading due to load chunks limit.
// return reader, ErrOverChunkLimit
return reader, nil
Expand Down
20 changes: 11 additions & 9 deletions oviewer/oviewer.go
Expand Up @@ -208,10 +208,10 @@ type Config struct {
// General represents the general behavior.
General general

// LoadChunksLimit is a number that limits chunk loading.
LoadChunksLimit int
// FileLoadChunksLimit is a number that limits the chunks loading a file into memory.
FileLoadChunksLimit int
// MemoryLimit is a number that limits chunk loading.
MemoryLimit int
// MemoryLimitFile is a number that limits the chunks loading a file into memory.
MemoryLimitFile int
// BeforeWriteOriginal specifies the number of lines before the current position.
// 0 is the top of the current screen
BeforeWriteOriginal int
Expand Down Expand Up @@ -284,10 +284,10 @@ type OVStyle struct {
}

var (
// LoadChunksLimit is a number that limits the chunks to load into memory.
LoadChunksLimit int
// FileLoadChunksLimit is a number that limits the chunks loading a file into memory.
FileLoadChunksLimit int
// MemoryLimit is a number that limits the chunks to load into memory.
MemoryLimit int
// MemoryLimitFile is a number that limits the chunks loading a file into memory.
MemoryLimitFile int

// OverStrikeStyle represents the overstrike style.
OverStrikeStyle tcell.Style
Expand Down Expand Up @@ -920,9 +920,11 @@ func (root *Root) debugNumOfChunk() {
if !root.Debug {
return
}
log.Println("MemoryLimit:", root.MemoryLimit)
log.Println("MemoryLimitFile:", root.MemoryLimitFile)
for _, doc := range root.DocList {
if !doc.seekable {
if LoadChunksLimit > 0 {
if MemoryLimit > 0 {
log.Printf("%s: The number of chunks is %d, of which %d(%v) are loaded", doc.FileName, len(doc.chunks), doc.loadedChunks.Len(), doc.loadedChunks.Keys())
}
continue
Expand Down
8 changes: 4 additions & 4 deletions oviewer/reader.go
Expand Up @@ -125,10 +125,6 @@ func (m *Document) searchRead(reader *bufio.Reader, chunkNum int, searcher Searc
func (m *Document) loadRead(reader *bufio.Reader, chunkNum int) (*bufio.Reader, error) {
if m.seekable {
m.currentChunk = chunkNum
if len(m.chunks[chunkNum].lines) != 0 {
// already loaded.
return reader, nil
}
return m.loadReadFile(reader, chunkNum)
}
return m.loadReadMem(reader, chunkNum)
Expand All @@ -139,6 +135,10 @@ func (m *Document) loadRead(reader *bufio.Reader, chunkNum int) (*bufio.Reader,
func (m *Document) loadReadFile(reader *bufio.Reader, chunkNum int) (*bufio.Reader, error) {
_ = m.evictChunksFile(chunkNum)
m.addChunksFile(chunkNum)
if len(m.chunks[chunkNum].lines) != 0 {
// already loaded.
return reader, nil
}
return m.readChunk(reader, chunkNum)
}

Expand Down

0 comments on commit 0b2e4ab

Please sign in to comment.