jjzjj

go - 内存有效的方式

coder 2024-07-11 原文

我有两个用 Go 编写的类似程序的例子。该代码的主要目的是使用结构中的值对结构映射进行排序。

带指针的例子

package main

import (
    "fmt"
    "sort"
)

type payload struct {
    data string
    value  float64
}

type container struct {
    counter int
    storage map[int]*payload
}

type payloadSlice []*payload

// Len is part of sort.Interface.
func (p payloadSlice) Len() int {
    return len(p)
}

// Swap is part of sort.Interface.
func (p payloadSlice) Swap(i, j int) {
    p[i], p[j] = p[j], p[i]
}

// Less is part of sort.Interface. We use count as the value to sort by
func (p payloadSlice) Less(i, j int) bool {
    return p[i].value < p[j].value
}
func main() {
    name := "special_unique_name"
    var m = map[string]container{
        name: {counter: 10, storage: map[int]*payload{
            5: {data: "epsilon", value: 55},8: {data: "theta", value: 85},4: {data: "delta", value: 48},1: {data: "alpha", value: 14},10: {data: "kappa", value: 101},
            3: {data: "gamma", value: 31},6: {data: "zeta", value: 63},2: {data: "beta", value: 26},9: {data: "iota", value: 92},7: {data: "eta", value: 79},
        }},
    }
    s := make(payloadSlice, 0, len(m[name].storage))
    for _, v := range m[name].storage {
        s = append(s, v)
    }
    sort.Sort(s)

    for _, v := range s {
        fmt.Println(name, v)
    }
}

有值的例子

package main

import (
    "fmt"
    "sort"
)

type payload struct {
    data string
    value  float64
}

type container struct {
    counter int
    storage map[int]payload
}

type payloadSlice []payload

// Len is part of sort.Interface.
func (p payloadSlice) Len() int {
    return len(p)
}

// Swap is part of sort.Interface.
func (p payloadSlice) Swap(i, j int) {
    p[i], p[j] = p[j], p[i]
}

// Less is part of sort.Interface. We use count as the value to sort by
func (p payloadSlice) Less(i, j int) bool {
    return p[i].value < p[j].value
}
func main() {
    name := "special_unique_name"
    var m = map[string]container{
        name: {counter: 10, storage: map[int]payload{
            5: {data: "epsilon", value: 55},8: {data: "theta", value: 85},4: {data: "delta", value: 48},1: {data: "alpha", value: 14},10: {data: "kappa", value: 101},
            3: {data: "gamma", value: 31},6: {data: "zeta", value: 63},2: {data: "beta", value: 26},9: {data: "iota", value: 92},7: {data: "eta", value: 79},
        }},
    }
    s := make(payloadSlice, 0, len(m[name].storage))
    for _, v := range m[name].storage {
        s = append(s, v)
    }
    sort.Sort(s)

    for _, v := range s {
        fmt.Println(name, v)
    }
}

我想知道 2 个时刻:

  1. 哪个示例会节省内存? (我猜这是一个指针方式)

  2. 如何使用具有不同数量结构的测试数据来衡量这些示例的性能?你能帮我创建 Benchmark 吗?

我想映射中每个结构的大小平均在 1-2kB 之间变化。

最佳答案

“内存高效”是一个非常宽泛的术语,在垃圾收集语言(如具有独立堆和堆栈的 Go)中可能意味着几个非常不同的东西:

  • 什么占用的内存最少?
  • 什么造成的 GC 压力最小?

如果您想尽量减少应用程序的占用空间,您可能希望在任何时候在多个范围(例如多个函数)中使用一个值时使用指针。这减少了复制,但增加了等于指针大小的开销(在 64 位系统上为 8 个字节)。

如果您想最大程度地减少 GC 压力,您可能只想在需要指针语义或基础值非常大时才使用指针。指针强制将值放到堆上,这是垃圾回收的对象,而值可以保留在堆栈上,而不是(当函数返回时,堆栈被整体销毁,这是线程安全的,需要没有引用跟踪)。

“GC 压力”是指在堆上创建和销毁的东西越多,垃圾收集器必须做的工作就越多,这会占用处理器时间,而不是应用程序正在执行的实际工作。每次在堆上分配时,如果没有空间容纳新值,垃圾收集器将尝试通过在堆上查找不再需要的值来释放空间。您在堆上分配的越多,GC 运行的频率就越高,运行时间也就越长。

对于您的第二个问题,您可以(并且应该!)使用 benchmarking facility of the testing package 衡量针对您的特定情况的各种方法的性能。 .确保使用真实的数据和操作进行测试;使用“虚拟”数据类型的微基准测试或基准测试不太可能产生任何有值(value)的数据。该包的文档,以及通过网络搜索很容易找到的无数博客文章和教程,应该可以指导您正确地了解如何在 Go 中编写和使用基准测试。

在您的特定情况下,请记住您的数据类型 - 就此问题而言 - 比您想象的要小:在 64 位系统上为 24 字节,无论字符串的长度如何。为什么?因为 string 在内部是一个包含 int 长度和指向底层字节的指针的结构。当您尝试优化内存使用时,请记住字符串、 slice (但不是数组!)和映射都是非常小的结构,包含指向其底层数据的指针。

最重要的是:过早的优化是万恶之源。您应该为两件事编写代码:功能和可读性。当它们提供您需要的功能时使用指针语义,并且使用起来具有直观意义。如果您测量资源问题(CPU 或内存),那么您应该分析您的应用程序以找到问题的根源,确定它们的优先级并优化它们。

在您测量和分析性能问题之前,您不会遇到性能问题。

关于go - 内存有效的方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55165798/

有关go - 内存有效的方式的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 如何进行排列以有效地定制输出 - 2

    这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][

  5. ruby-on-rails - Ruby 中的内存模型 - 2

    ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序

  6. ruby-on-rails - 正确的 Rails 2.1 做事方式 - 2

    question的一些答案关于redirect_to让我想到了其他一些问题。基本上,我正在使用Rails2.1编写博客应用程序。我一直在尝试自己完成大部分工作(因为我对Rails有所了解),但在需要时会引用Internet上的教程和引用资料。我设法让一个简单的博客正常运行,然后我尝试添加评论。靠我自己,我设法让它进入了可以从script/console添加评论的阶段,但我无法让表单正常工作。我遵循的其中一个教程建议在帖子Controller中创建一个“评论”操作,以添加评论。我的问题是:这是“标准”方式吗?我的另一个问题的答案之一似乎暗示应该有一个CommentsController参

  7. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  8. python - 是否可以使用 Ruby 或 Python 禁用 anchor /引用来发出有效的 YAML? - 2

    是否可以在PyYAML或Ruby的Psych引擎中禁用创建anchor和引用(并有效地显式列出冗余数据)?也许我在网上搜索时遗漏了一些东西,但在Psych中似乎没有太多可用的选项,而且我也无法确定PyYAML是否允许这样做.基本原理是我必须序列化一些数据并将其以可读的形式传递给一个不是真正的技术同事进行手动验证。有些数据是多余的,但我需要以最明确的方式列出它们以提高可读性(anchor和引用是提高效率的好概念,但不是人类可读性)。Ruby和Python是我选择的工具,但如果有其他一些相当简单的方法来“展开”YAML文档,它可能就可以了。 最佳答案

  9. ruby - 鸭子输入字符串、符号和数组的优雅方式? - 2

    这是针对我无法破坏的现有公共(public)API,但我确实希望对其进行扩展。目前,该方法采用字符串或符号或任何其他在作为第一个参数传递给send时有意义的内容我想添加发送字符串、符号等列表的功能。我可以只使用is_a吗?数组,但还有其他发送列表的方法,这不是很像ruby​​。我将调用列表中的map,所以第一个倾向是使用respond_to?:map。但是字符串也会响应:map,所以这行不通。 最佳答案 如何将它们全部视为数组?String的行为与仅包含String的Array相同:deffoo(obj,arg)[*arg].eac

  10. 键删除后 ruby​​ 哈希内存泄漏 - 2

    你好,我无法成功如何在散列中删除key后释放内存。当我从哈希中删除键时,内存不会释放,也不会在手动调用GC.start后释放。当从Hash中删除键并且这些对象在某处泄漏时,这是预期的行为还是GC不释放内存?如何在Ruby中删除Hash中的键并在内存中取消分配它?例子:irb(main):001:0>`ps-orss=-p#{Process.pid}`.to_i=>4748irb(main):002:0>a={}=>{}irb(main):003:0>1000000.times{|i|a[i]="test#{i}"}=>1000000irb(main):004:0>`ps-orss=-p

随机推荐