Best way to trim first N number of elements in array

xuanbao · · 16 次点击    
<p>hello - currently thinking about doing something like this <a href="" rel="nofollow"></a> to trim the first n number of elements from an array and I was wanting to make sure that is the best way of doing it. From what I understand of slices and arrays in go - I&#39;m creating a new slice that only references the 2nd part of the array, and as a result the underlying elements in the array won&#39;t get gc until append creates a new array and only copies the elements currently being referenced by the slice. In the real world the trimming will happen concurrently with the appending.</p> <p>Is this the best way of doing this? </p> <hr/>**评论:**<br/><br/>MalkMalice: <pre><p>I&#39;m a go newbie myself so I can&#39;t give you the correct answer. But I observed something else: <a href="" rel="nofollow"></a> You don&#39;t need to slice this way <code>arr = arr[2:len(arr)]</code> Drop the <code>len(arr)</code> and just do <code>arr = arr[2:]</code></p></pre>not_another_throw_aw: <pre><p>Thanks :) </p></pre>niosop: <pre><p>You are correct in how it works. See <a href="" rel="nofollow"></a> and <a href="" rel="nofollow"></a> for more info.</p> <p>As for if it&#39;s the best way of doing it, it depends on what you&#39;re trying to achieve. For some situations a <a href="" rel="nofollow">ring buffer</a> might work well for you. Or a linked list. Or some other container. Really depends on your access patterns and desired performance characteristics.</p></pre>nsd433: <pre><p>A agree. For quick &amp; dirty ring buffers slicing off the head and append()ing the tail is easy to read and is reasonably efficient [1].</p> <p>Do remember when slicing off the head to zero the head value if it contains references, so the referenced values can be gc&#39;ed. Otherwise they can&#39;t be gc&#39;ed until append() reached the slice&#39;s capacity and reallocates a new array.</p> <p>[1] every N = <code>capacity of slice</code> items you allocate a new array, copy the live items, and throw the old array away to the gc&#39;ed. If it isn&#39;t a tight loop and the number of live items (len(slice)) isn&#39;t large, the overhead is noise.</p></pre>chmikes: <pre><p>Note that it may be faster to copy the data to the front of the buffer even-though it is counter intuitive that it&#39;s faster. </p> <p>arr = arr[:copy(arr, arr[2:])]</p> <p>This is simpler and allows to resort the data after new data appending. Sorting data in a circular buffer requires special care. </p> <p>Explanation of the above code. arr[2:] drops the first two elements of the array. copy() copies the elements from the subslice arr[2:] to the array arr and returns the number of elements copied. It is len(arr) - 2. We then set arr to be arr[:len(arr)-2] witch is the subslice with the last two elements dropped. </p> <p>This copy the len(arr)-2 elements at the end of arr to the front of the array arr and drops the last two elements. </p></pre>nsd433: <pre><p>I like that. It&#39;s the same # of items copied as letting append() do the copy, but doesn&#39;t create any garbage.</p> <p>I guess the only downside is you have to keep track of the original slice, so you can pass it into copy(). With the append()-and-let-it-realloc technique you only need the slice of live items.</p></pre>TheMerovius: <pre><blockquote> <p>In the real world the trimming will happen concurrently with the appending.</p> </blockquote> <p>No. Use locking. You can&#39;t modify a slice concurrently.</p></pre>not_another_throw_aw: <pre><p>yea - I was keeping it simple in the playground </p></pre>TheMerovius: <pre><p>I don&#39;t know what that means. &#34;Keeping it simple&#34; in the playground is one thing, but you also said, that you intend to do the modification concurrently &#34;in the real world&#34;. That is not possible (I mean, it&#39;s possible, it just will lead likely to crashes and unsafe behavior).</p> <p>Do not modify slices concurrently. Use locking. If you <em>do</em> use locking, any mention of concurrency is a red herring (as it&#39;s equivalent to just doing the modifications sequentially).</p></pre>F41LUR3: <pre><p>If I&#39;m not mistaken you&#39;re not using an array as far as I can tell, because you didn&#39;t specify the array length. So that is actually a slice.</p></pre>not_another_throw_aw: <pre><p>Somewhere I believe there is an array being referenced because a slice must reference an array. The references I&#39;m using are probably slices but those slices reference arrays at some point. At least that&#39;s how I understand it, but I could be wrong. </p></pre>F41LUR3: <pre><p>Slices are backed by an array, but are not an array themselves. Arrays are a type unto themselves (including their length), and are set at compile time. Meaning that the array size must be declared in literal form, or using the implicit <code>[...]</code> identifier where the compiler will determine the size of the array based on the count of the values you initialize in the array with.</p> <p>ie:</p> <pre><code>[...]int{1, 2, 3} // would give you an array of type [3]int </code></pre> <p>In short, arrays must always have a compile-time defined size, as they are considered a type. So leaving that blank <code>[]int{}</code> is actually a slice and not an array.</p></pre>ops-man: <pre><p>go newbie here as well. this post is cause for a question of my own. since he didn&#39;t explicitly declare an array or if you don&#39;t, would the underlying array elements that he describes as trimming be GC&#39;d sooner - in other words is it advantageous to not declare the array. </p></pre>nsd433: <pre><p>There is no difference between</p> <pre><code>x := make([]int, 10) </code></pre> <p>and</p> <pre><code>var y [10]int x := y[0:10] </code></pre> <p>Except that in the latter case the array has a name (y).</p> <p>GC of the array happens when there are no more references to it. Those references can be slices which slice the array, or pointers to the array, or pointers to elements of the array.</p></pre>epiris: <pre><p>In most cases you probably don&#39;t need to think much about this. But if you know it&#39;s a very large slice AND you are no longer going to use the elements that do not belong to your new slice it might be worth making a new one and just copying. I.e: </p> <pre><code>src := s[1024:2048] dst := make([]byte, 1024) copy(dst, src) // src and dst can be collected independently </code></pre></pre>JackOhBlades: <pre><p>Go is about types. What I would do: </p> <pre><code>type ConcurrentList struct { m *sync.Mutex Data int[] } func (l ConcurrentList) Trim(len int) { l.m.Lock() defer l.m.Unlock() // do some trimming } func (l ConcurrentList) Append(el int) { l.m.Lock() defer l.m.Unlock() // do some appending } </code></pre> <p>Your implementations could be what you have in the playground, it&#39;s up to you. </p> <p>While it doesn&#39;t directly answer your question I hope it helps.</p></pre>dchapes: <pre><p>You almost never want to export the mutex to consumers like that.</p> <p><del>(PS: check how your post is formatted after saving and edit if required, there is a &#34;formatting help&#34; button/link on the bottom of the edit box).</del></p></pre>JackOhBlades: <pre><p>True that. Edited. It looked fine on my phone! Thanks :)</p></pre>
16 次点击  
加入收藏 微博
0 回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet