C# 将唯一阵列元素移动到前端并切片阵列的最快方法
我有一个函数,它获取一个指向大小为4(始终)的数组的指针,并且必须只分割唯一的元素并返回它们。这个函数每秒被调用100k次,这就是为什么它需要快速,并且不需要对GC施加太大压力(这就是为什么缓冲区变量是指针-堆栈alloc)C# 将唯一阵列元素移动到前端并切片阵列的最快方法,c#,arrays,performance,pointers,unsafe,C#,Arrays,Performance,Pointers,Unsafe,我有一个函数,它获取一个指向大小为4(始终)的数组的指针,并且必须只分割唯一的元素并返回它们。这个函数每秒被调用100k次,这就是为什么它需要快速,并且不需要对GC施加太大压力(这就是为什么缓冲区变量是指针-堆栈alloc) private const int BufferLength=4; 私有静态不安全ReadOnlySpan SliceUnique(int*buffer) { //TODO:将唯一元素移到前面 //并获取最后一个唯一索引 返回新的ReadOnlySpan(缓冲区,lastU
private const int BufferLength=4;
私有静态不安全ReadOnlySpan SliceUnique(int*buffer)
{
//TODO:将唯一元素移到前面
//并获取最后一个唯一索引
返回新的ReadOnlySpan(缓冲区,lastUniqueIndex+1);
}
//例如:
//输入:[1,3,2,1]|[1,4,4,4]
//输出:[1,3,2](顺序无关紧要)|[1,4]
对于数组[a、b、c、d]:
if (a==b)
if (b==c)
if (c==d)
return (a,1)
else
return (a,d,2)
else
if (c==d)
return (a,c,2)
else
if (a==d)
return (a,c,2)
else
return (a,c,d,3)
else
if (b==c)
if (c==d)
return (a,b,2)
else
if (a==d)
return (a,b,2)
else
return (a,b,d,3)
else
if (c==d)
if (a==c)
return (a,b,2)
else
return (a,b,c,3)
else
if (a==c)
if (b==d)
return (a,b,2)
else
return (a,b,d,3)
else
if (b==d)
return (a,b,c,3)
else
if (a==d)
return (a,b,c,3)
else
return (a,b,c,d,4)
。。。你明白了。。。刚刚写下了我的想法,所以最好对所有可能的输入进行单元测试:-)这是我的想法(当问题是关于int数组时,但我确信它可以修改为处理
ReadOnlySpan
(不管是什么…)在我的计算机上测试,1000000个数组(包括创建数组)的平均执行时间为195毫秒: 测试代码:
static void Main(string[] args)
{
long sum = 0;
var stopwatch = new System.Diagnostics.Stopwatch();
var numberOfTimes = 1000 * 1000;
for (int i = 0; i < 100; i++)
{
stopwatch.Restart();
for (int j = 0; j < numberOfTimes; j++)
{
var a = new int[] { j % 2, j % 3, j % 4, j % 5 };
var r = RemoveDuplicates(a);
}
stopwatch.Stop();
sum += stopwatch.ElapsedMilliseconds;
// Console.WriteLine(string.Format("{0} => {1}", string.Join(",", a), string.Join(",", r)));
}
double avg = sum / 100;
Console.WriteLine("Average execution time (100 executions) is {0}", avg);
Console.ReadKey();
}
static void Main(字符串[]args)
{
长和=0;
var stopwatch=new System.Diagnostics.stopwatch();
变量次数=1000*1000;
对于(int i=0;i<100;i++)
{
stopwatch.Restart();
对于(int j=0;j{1}”、string.Join(“,”,a)、string.Join(“,”,r));
}
双平均值=总和/100;
WriteLine(“平均执行时间(100次执行)为{0}”,平均值);
Console.ReadKey();
}
@ZoharPeled答案-使用ReadOnlySpan进行优化
private static unsafe ReadOnlySpan<int> SliceUniqueZoharPeledOptimized(int* buffer)
{
bool remove1 = buffer[1] == buffer[0];
bool remove2 = buffer[2] == buffer[1] || buffer[2] == buffer[0];
bool remove3 = buffer[3] == buffer[2] || buffer[3] == buffer[1] || buffer[3] == buffer[0];
if (remove1 && remove2 && remove3)
{
return new ReadOnlySpan<int>(buffer, 1);
}
if (remove1 && remove2)
{
buffer[1] = buffer[3];
return new ReadOnlySpan<int>(buffer, 2);
}
if (remove1 && remove3)
{
buffer[1] = buffer[2];
return new ReadOnlySpan<int>(buffer, 2);
}
if (remove2 && remove3)
{
return new ReadOnlySpan<int>(buffer, 2);
}
if (remove1)
{
buffer[1] = buffer[3];
return new ReadOnlySpan<int>(buffer, 3);
}
if (remove2)
{
buffer[2] = buffer[3];
return new ReadOnlySpan<int>(buffer, 3);
}
if (remove3)
{
return new ReadOnlySpan<int>(buffer, 3);
}
return new ReadOnlySpan<int>(buffer, 4);
}
private static unsafe ReadOnlySpan SliceUniqueZoharPeledOptimized(int*buffer)
{
bool remove1=缓冲区[1]==缓冲区[0];
bool remove2=缓冲区[2]==缓冲区[1]| |缓冲区[2]==缓冲区[0];
bool remove3=缓冲区[3]==缓冲区[2]| |缓冲区[3]==缓冲区[1]| |缓冲区[3]==缓冲区[0];
如果(删除1和删除2和删除3)
{
返回新的ReadOnlySpan(缓冲区,1);
}
如果(删除1和删除2)
{
缓冲区[1]=缓冲区[3];
返回新的ReadOnlySpan(缓冲区,2);
}
如果(删除1和删除3)
{
缓冲区[1]=缓冲区[2];
返回新的ReadOnlySpan(缓冲区,2);
}
如果(移除2和移除3)
{
返回新的ReadOnlySpan(缓冲区,2);
}
如果(删除1)
{
缓冲区[1]=缓冲区[3];
返回新的ReadOnlySpan(缓冲区,3);
}
如果(删除2)
{
缓冲区[2]=缓冲区[3];
返回新的ReadOnlySpan(缓冲区,3);
}
如果(删除3)
{
返回新的ReadOnlySpan(缓冲区,3);
}
返回新的ReadOnlySpan(缓冲区,4);
}
您能否提供一两个应该产生的输入和结果示例。(特别是定义哪些元素不是唯一的)。@Richard input:[1,3,3,4]->[1,4,3]或[1,3,4](顺序不重要),input[2323234321,11,11]->[2323234321,11]始终是四个元素吗?如果只是一堆if
s呢?不会有那么多不同的输入…@Haukinger yes总是4。也许如果对长度为4的数组进行某种优化,它不会得到所有情况->示例:预期3,结果:4,3_4_3_2预期2,结果:4,4_2_4_2预期3,结果:4,3_4_2_3预期3,结果:4,3_1_4_1预期3,结果:4,1_3_4_1预期3,结果:4,3_4_2_3预期3,结果:4,3_2_1_3预期3,结果:4,2_4_3_2在使用ReadOnlySpan选择您的答案后,我已将执行时间缩短为两个,并删除了不必要的分配很高兴提供帮助:-)您能分享一下您是如何优化它的吗?@321X ptp已经分享了他自己问题的最终解决方案。啊,是的,我明白了!我的错!我不得不向下滚动。。。doh
static void Main(string[] args)
{
long sum = 0;
var stopwatch = new System.Diagnostics.Stopwatch();
var numberOfTimes = 1000 * 1000;
for (int i = 0; i < 100; i++)
{
stopwatch.Restart();
for (int j = 0; j < numberOfTimes; j++)
{
var a = new int[] { j % 2, j % 3, j % 4, j % 5 };
var r = RemoveDuplicates(a);
}
stopwatch.Stop();
sum += stopwatch.ElapsedMilliseconds;
// Console.WriteLine(string.Format("{0} => {1}", string.Join(",", a), string.Join(",", r)));
}
double avg = sum / 100;
Console.WriteLine("Average execution time (100 executions) is {0}", avg);
Console.ReadKey();
}
private static unsafe ReadOnlySpan<int> SliceUniqueZoharPeledOptimized(int* buffer)
{
bool remove1 = buffer[1] == buffer[0];
bool remove2 = buffer[2] == buffer[1] || buffer[2] == buffer[0];
bool remove3 = buffer[3] == buffer[2] || buffer[3] == buffer[1] || buffer[3] == buffer[0];
if (remove1 && remove2 && remove3)
{
return new ReadOnlySpan<int>(buffer, 1);
}
if (remove1 && remove2)
{
buffer[1] = buffer[3];
return new ReadOnlySpan<int>(buffer, 2);
}
if (remove1 && remove3)
{
buffer[1] = buffer[2];
return new ReadOnlySpan<int>(buffer, 2);
}
if (remove2 && remove3)
{
return new ReadOnlySpan<int>(buffer, 2);
}
if (remove1)
{
buffer[1] = buffer[3];
return new ReadOnlySpan<int>(buffer, 3);
}
if (remove2)
{
buffer[2] = buffer[3];
return new ReadOnlySpan<int>(buffer, 3);
}
if (remove3)
{
return new ReadOnlySpan<int>(buffer, 3);
}
return new ReadOnlySpan<int>(buffer, 4);
}