内存有效方式 [英] memory efficient way

查看:117
本文介绍了内存有效方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have two examples of the similar program written on Go. Main aim of that code is sort map of structs using value in the struct.

带有指针的示例

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. 哪个示例将提高内存效率? (我想这是一种指针方式)

  1. Which example will be memory-efficient? (I guess it's a pointer way)

如何使用地图中具有不同数量结构的测试数据来衡量这些示例的性能?您能帮我建立基准吗?

How to measure performance of these examples, using test data with with different number of structs inside the map? Can you help me with creating Benchmark?

我认为地图中每个结构的大小平均在1-2kB之间.

I suppose the size of each struct in the map will vary from 1-2kB in average.

推荐答案

内存高效"是一个相当宽泛的术语,在诸如Go之类的垃圾收集语言中,可能有两件事非常不同,它们具有单独的堆和堆栈:

"Memory-efficient" is a pretty broad term, and can mean a couple of very different things in a garbage-collected language like Go that has separate heap and stack:

  • 什么使用最少的内存?
  • 什么导致了最小的GC压力?

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

If you want to minimize the application's footprint, you probably want to use pointers any time that you use a value in multiple scopes (e.g. multiple functions). This reduces copying, but adds overhead equal to the pointer size (8 bytes on a 64-bit system).

如果要最大程度地降低GC压力,则可能仅在需要指针语义或基础值很大时才希望使用指针.指针将值强制到堆上,该值将进行垃圾回收,而值可以保留在堆栈上,而不是(当函数返回时,堆栈会被完全破坏,这是线程安全的,并且需要没有参考跟踪).

If you want to minimize GC pressure, you probably want to use pointers only when you need pointer semantics, or the underlying values are quite large. A pointer forces the value onto the heap, which is subject to garbage collection, while a value can be kept on the stack, which is not (when the function returns, the stack is destroyed in its entirety, which is thread-safe and requires no reference tracking).

"GC压力"的想法是,在堆上创建和销毁的对象越多,垃圾收集器要做的工作就越多,这使处理器时间与应用程序正在执行的实际工作相去甚远.每次在堆上分配时,如果没有空间容纳新值,则垃圾收集器将通过在堆上查找不再需要的值来尝试释放空间.您在堆上分配的次数越多,GC必须运行的频率就越高,这些运行将花费的时间就越长.

"GC pressure" is the idea that the more things are created and destroyed on the heap, the more work the garbage collector has to do, which takes processor time away from the real work your application is doing. Every time you allocate on the heap, if there isn't space for the new value, the garbage collector will try to free space by looking for values on the heap that are no longer needed. The more you allocate on the heap, the more often GC has to run, and the longer those runs will take.

对于第二个问题,您可以(并且应该!)使用

To your second question, you can (and should!) measure performance of various approaches to your specific circumstance using the benchmarking facility of the testing package. Make sure you test with realistic data and operations; microbenchmarks or benchmarks using "dummy" data types are unlikely to yield data of any value. The documentation for that package, and countless blog posts and tutorials easily found by web search, should guide you in the right direction on how to write and use benchmarks in Go.

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

In your specific case, bear in mind that your data type is - as far as this question is concerned - smaller than you think: 24 bytes on a 64-bit system, regardless of the length of the string. Why? Because a string, internally, is a struct containing an int for the length and a pointer to the underlying bytes. When you're trying to optimize for memory use, remember that strings, slices (but not arrays!), and maps are all very small structs containing pointers to their underlying data.

最重要的是:过早的优化是万恶之源.您应该为两件事编写代码:功能和可读性.当指针提供您所需的功能并使用直观时,请使用指针语义.如果您测量资源问题(CPU或内存),请然后配置您的应用程序,以找出问题的根源,确定优先级并对其进行优化.

And most importantly: premature optimization is the root of all evil. You should be writing code for two things: functionality, and readability. Use pointer semantics when they deliver the functionality you need, and make intuitive sense to use. If you measure a resource problem (CPU or memory), then you should profile your application to find the sources of the problem, prioritize them, and optimize them.

直到您测量并分析了性能问题,才没有性能问题.

Until you have measured and profiled a performance problem, you do not have a performance problem.

这篇关于内存有效方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆