Вид предостережения / предположения впереди:
- Похоже, вы используете только
int
в качестве типа данных (или, по крайней мере, все в порядке с использованием Buffer.BlockCopy
, что подразумевает, что вы можете вообще работать с примитивными типами).
- Что касается тестовых данных, которые вы показываете, я не думаю, что при использовании какого-то вменяемого подхода все будет по-другому.
С учетом вышесказанного следующая реализация (которая должна быть специализированной для конкретного примитивного типа (здесь int
), поскольку она использует fixed
) примерно в 10 раз быстрее, чем подход с использованием внутреннего цикла:
unsafe public static int[][] AsJagged2(int[,] rect)
{
int row1 = rect.GetLowerBound(0);
int rowN = rect.GetUpperBound(0);
int col1 = rect.GetLowerBound(1);
int colN = rect.GetUpperBound(1);
int height = rowN - row1 + 1;
int width = colN - col1 + 1;
int[][] jagged = new int[height][];
int k = 0;
for (int i = row1; i < row1 + height; i++)
{
int[] temp = new int[width];
fixed (int *dest = temp, src = &rect[i, col1])
{
MoveMemory(dest, src, rowN * sizeof(int));
}
jagged[k++] = temp;
}
return jagged;
}
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
unsafe internal static extern void MoveMemory(void* dest, void* src, int length);
Используя следующий «тестовый код»:
static void Main(string[] args)
{
Random rand = new Random();
int[,] data = new int[100,1000];
for (int i = 0; i < data.GetLength(0); i++)
{
for (int j = 0; j < data.GetLength(1); j++)
{
data[i, j] = rand.Next(0, 1000);
}
}
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
int[][] dataJagged = AsJagged(data);
}
Console.WriteLine("AsJagged: " + sw.Elapsed);
sw = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
int[][] dataJagged2 = AsJagged2(data);
}
Console.WriteLine("AsJagged2: " + sw.Elapsed);
}
Где AsJagged
(первый случай) - ваша исходная функция, я получаю следующий вывод:
AsJagged: 00:00:00.9504376
AsJagged2: 00:00:00.0860492
Таким образом, действительно существует более быстрый способ сделать это, однако, в зависимости от размера тестовых данных, количества раз, которое вы фактически выполняете эту операцию, и вашей готовности разрешить небезопасный и P / Invoke код, вы Вам, вероятно, это не понадобится .
С учетом вышесказанного мы использовали большие матрицы double
(скажем, 7000x10000 элементов), где это действительно имело огромное значение.
Обновление: об использовании Buffer.BlockCopy
Я мог бы пропустить Marshal
или другой трюк, но я не думаю, что здесь можно использовать Buffer.BlockCopy
. Это связано с тем, что для него требуется, чтобы массив источника и назначения был, ну, Array
.
В нашем примере местом назначения является массив (например, int[] temp = ...
), а источником - нет. Хотя мы «знаем», что для двумерных массивов примитивных типов макет таков, что каждая «строка» (т. Е. Первое измерение) является массивом типа в памяти, не существует безопасного (как в unsafe
) способа получить этот массив без затрат на копирование в первую очередь. Таким образом, нам в основном нужно использовать функцию, которая просто работает с памятью и не заботится о фактическом ее содержании - например, MoveMemory
. Кстати, внутренняя реализация Buffer.BlockCopy
делает нечто подобное.