Почему устаревший "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()?