【问题标题】:Fastest way to find number of lines in go?查找行数的最快方法是什么?
【发布时间】:2018-04-24 16:57:36
【问题描述】:

我现在正在使用的:

numlines := strings.Count(editor.Text(), "\n")
fmt.Print(strconv.Itoa(numlines))
message.SetText(strconv.Itoa(numlines))

每当更新文本框时都会运行此程序。哪种方式最适合执行此操作?

【问题讨论】:

    标签: string performance go


    【解决方案1】:

    这很好。但不要忘记,如果最后一个字符不是换行符,则必须将出现次数加 1,因为这将是行数(最后一行可能不以换行符结尾)。

    我们可能会认为,由于您要计算的子字符串只有一个字符(单个 rune),我们可以创建一个自定义解决方案,只计算这个单个字符的出现次数(而不是计算子字符串)。它可能看起来像这样:

    func countRune(s string, r rune) int {
        count := 0
        for _, c := range s {
            if c == r {
                count++
            }
        }
        return count
    }
    

    string 值上的 for range 迭代其 runes。)

    并对其进行测试(在Go Playground 上尝试):

    fmt.Println(countRune("asdf\nasdf\nasdf\n", '\n')) // Prints 3
    

    实际上,这不会更快地计算换行符,因为这是 UTF-8 编码中的单个 byte,并且 strings.Count() 已经针对子字符串长度为 1 的子字符串进行了优化:

    // Count counts the number of non-overlapping instances of substr in s.
    // If substr is an empty string, Count returns 1 + the number of Unicode code points in s.
    func Count(s, substr string) int {
        if len(substr) == 1 && cpu.X86.HasPOPCNT {
            return countByte(s, byte(substr[0]))
        }
        return countGeneric(s, substr)
    }
    
    func countByte(s string, c byte) int // ../runtime/asm_amd64.s
    

    如果您可以访问编辑器的“内部”字节或符文数组,则可以提高此操作(计算行数)的性能,因此您不必调用其 Text() 方法,该方法创建和返回其内容的副本。

    【讨论】:

      【解决方案2】:

      当您询问有关最快方法的问题时,您应该使用 Go testing 包基准测试工具进行测量。

      例如,使用strings.Count 计算lorem ipsum 中的行数,并在string 上使用for range 循环,并测量从byte 切片转换为string 的任何额外成本。您可以通过计算byte 切片中的行数来避免任何byte 切片到string 的开销。

      $ gotest lines_test.go -bench=.
      data: /home/peter/shakespeare.pg100.txt 5589889
      BenchmarkStringCount-4     30000000    57.3 ns/op     0 B/op   0 allocs/op
      BenchmarkStringByRune-4     3000000   563 ns/op       0 B/op   0 allocs/op
      BenchmarkBytesToString-4   10000000   173 ns/op     480 B/op   1 allocs/op
      BenchmarkBytesCount-4      20000000    61.2 ns/op     0 B/op   0 allocs/op
      

      lines_test.go:

      package main
      
      import (
          "bytes"
          "strconv"
          "strings"
          "testing"
      )
      
      func linesStringCount(s string) string {
          n := strings.Count(s, "\n")
          if len(s) > 0 && !strings.HasSuffix(s, "\n") {
              n++
          }
          return strconv.Itoa(n)
      }
      
      func linesStringByRune(s string) string {
          n := 0
          for _, r := range s {
              if r == '\n' {
                  n++
              }
          }
          if len(s) > 0 && !strings.HasSuffix(s, "\n") {
              n++
          }
          return strconv.Itoa(n)
      }
      
      func linesBytesCount(s []byte) string {
          nl := []byte{'\n'}
          n := bytes.Count(s, nl)
          if len(s) > 0 && !bytes.HasSuffix(s, nl) {
              n++
          }
          return strconv.Itoa(n)
      }
      
      var data = []byte(`Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
      sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
      Ut enim ad minim veniam, 
      quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
      Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
      Excepteur sint occaecat cupidatat non proident, 
      sunt in culpa qui officia deserunt mollit anim id est laborum.`)
      
      func BenchmarkStringCount(b *testing.B) {
          text := string(data)
          b.ReportAllocs()
          b.ResetTimer()
          for i := 0; i < b.N; i++ {
              _ = linesStringCount(text)
          }
      }
      
      func BenchmarkStringByRune(b *testing.B) {
          text := string(data)
          b.ReportAllocs()
          b.ResetTimer()
          for i := 0; i < b.N; i++ {
              _ = linesStringByRune(text)
          }
      }
      
      func BenchmarkBytesToText(b *testing.B) {
          b.ReportAllocs()
          b.ResetTimer()
          for i := 0; i < b.N; i++ {
              _ = string(data)
          }
      }
      
      func BenchmarkBytesCount(b *testing.B) {
          text := data
          b.ReportAllocs()
          b.ResetTimer()
          for i := 0; i < b.N; i++ {
              _ = linesBytesCount(text)
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-14
        • 1970-01-01
        • 2011-04-23
        • 2020-05-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多