Почему устаревший "rsvg_pixbuf_from_file_at_size" быстрее / эффективнее, чем недекларированный метод (Каир)?

Я использую C# и P/Invoke для доступа к библиотекам GDK. Моя цель - преобразовать набор файлов SVG в растровые изображения (в частности, png), и использование библиотек GDK представляется наиболее надежным / точным.

После прочтения документации по Gnome/Cairo я нашел два основных подхода для достижения этой цели. Один из этих подходов использует устаревшие функции, а другой - нет.

Первый подход, который устарел, но, возможно, более прост / понятен, выглядит в основном так:

bool result = false;
ptrPixbuf = rsvg_pixbuf_from_file_at_size(svgFilePath, width, height, out ptrError);
if (ptrError == UIntPtr.Zero && ptrPixbuf != UIntPtr.Zero)
{
    bool isSaved = gdk_pixbuf_save(ptrPixbuf, outputFilePath, out ptrError, UIntPtr.Zero);
    if (ptrError == UIntPtr.Zero && isSaved == true)
    {
        result = true;
    }
}
return result;

Второй подход, который не является устаревшим, заключается в настройке Cairo Surface, его рендеринге и сохранении в файл. Это выглядит в основном так:

bool result = false;
ptrRsvgHandle = rsvg_handle_new_from_file(svgFilePath, out ptrError);
if (ptrError == UIntPtr.Zero)
{
    ptrCairoSurface = cairo_image_surface_create(CairoFormat.CAIRO_FORMAT_ARGB32, width, height);
    if ((cairo_surface_status(ptrCairoSurface) == CairoStatus.CAIRO_STATUS_SUCCESS)
    {
        ptrcairoContext = cairo_create(ptrCairoSurface);
        if ((cairo_status(ptrCairoContext) == CairoStatus.CAIRO_STATUS_SUCCESS))
        {
            bool isRendered = rsvg_handle_render_cairo(ptrRsvgHandle, ptrCairoContext);
            if (isRendered)
            {
                ptrPixbuf = rsvg_handle_get_pixbuf(ptrRsvgHandle);
                if (ptrPixbuf != UIntPtr.Zero)
                {
                    bool isSaved = gdk_pixbuf_save(ptrPixbuf, outputFilePath, out ptrError, UIntPtr.Zero);
                    if (ptrError == UIntPtr.Zero && isSaved == true)
                    {
                        result = true;
                    }
                }
            }
        }
    }
}
return result;

Кажется, что оба этих подхода работают - они генерируют правильный вывод растрового изображения (хотя у "Каирского пути" есть некоторые ошибки, когда я пытаюсь выполнить несколько операций параллельно - у меня заканчивается память).

У меня такой вопрос: почему старый / устаревший способ (rsvg_pixbuf_from_file_at_size) заметно быстрее, чем новый / каирский способ? Мои тесты показывают, что первый подход был более быстрым по всем направлениям (один файл / несколько файлов, стандартный C# ForEach/Parallel.ForEach).

Например, с 16 входными файлами (выходные размеры 6000x4200) и без параллельной обработки первый подход занимает ~2:15,89 секунды. Второй подход занимает около 2:37,95. При параллельной обработке (Parallel.Foreach вызывает мой код P/Invoke), результаты аналогичны - с MaxDegreesOfParallelism по умолчанию это занимает 30,7 секунды с устаревшим подходом и 36,95 с подходом Каира.

Каир, похоже, тоже использует значительно больше памяти. Кроме того, в дополнение к простому использованию большего количества ресурсов для каждого преобразования, Каир также, похоже, не знает, как избежать использования всей моей оперативной памяти. Например, если я увеличу количество входных файлов до 720 (с 16) и использую цикл Parallel.ForEach, у меня останется 0 МБ свободной оперативной памяти, и система остановится (в конце концов, процесс отладки завершается, и моя система возвращается... но она блокируется на минуту или около того).

Легкий ответ на мой вопрос - просто использовать устаревший подход, но почему он не рекомендуется? Каирский подход лучше чем-либо вообще?

Если кто-то захочет увидеть больше моего кода, дайте мне знать, и я добавлю его. Я попытался урезать код, который я разместил, до соответствующих битов (фактический код P/Invoke, а не код C#, вызывающий его).

1 ответ

Решение

Во-первых, ваша ошибка нехватки памяти, вероятно, происходит из-за утечек памяти в вашем коде. Ваш пример не вызывает никаких функций очистки (таких как cairo_destroy (), cairo_surface_destroy (), gdk_pixbuf_unref(), g_object_unref(), возможно что-то для любой возникающей ошибки GE).

Во-вторых, к чему в действительности обращается ваш второй код? Вы создаете поверхность и контекст Каира, отрисовываете rsvg, но затем вызываете rsvg_handle_get_pixbuf (), которая внутренне создает новую поверхность Каира и рисует все заново. Другими словами, разве вам не достаточно вызвать rsvg_handle_new_from_file(), rsvg_handle_get_pixbuf() и gdk_pixbuf_save()?

Другие вопросы по тегам