0

我试图了解我最近在我的项目中遇到的一个问题。我正在使用 Aurigma 库来调整图像大小。用于单线程模式,计算时只产生一个线程。最近我决定转向 ImageMagick 项目,因为它是免费和开源的。我已经在单线程模式下构建了 IM 并开始测试。起初我想在没有中断的情况下比较它们的性能,所以我创建了一个对进程及其线程具有高优先级的测试。另外,我将亲和力设置为一个核心。我得到那个 IM 比 Aurigma 快 25%。但是我添加的线程多于较少的 IM 对 Aurigma 有优势。

我的项目是一个 Windows 服务,它启动了大约 7-10 个子进程。每个进程有 2 个线程来处理图像。当我将测试作为两个不同的进程运行时,每个进程都有 2 个线程,我注意到 IM 的工作效率比 Aurigma 差大约 5%。

也许我的问题不是很详细,但这个范围对我来说有点新,我很乐意为进一步调查获得指导。如果一个程序在一个进程中的一个线程上运行,怎么会更快,但如果它同时在多个进程中运行,则更糟糕。

例如,

Au:8 个进程 x 2Th(每个线程 20 个任务)= 320 个任务,持续 245 秒

IM:8 个进程 x 2Th(每个线程 20 个任务)= 320 个任务,持续 280 秒

Au:4 个进程 x 2Th(每个线程 20 个任务)= 160 个任务,持续 121 秒

IM:4 个进程 x 2Th(每个线程 20 个任务)= 160 个任务,持续 141 秒

我们可以看到,如果我们有 1 个以上的进程,Au 工作得更好,但在单进程模式下:Au 处理一项任务 2.2 秒,IM 1.4 秒,IM 的总时间更好

private static void ThreadRunner(
        Action testFunc,
        int repeatCount,
        int threadCount
        )
    {
        WaitHandle[] handles = new WaitHandle[threadCount];

        var stopwatch = new Stopwatch();

        // warmup
        stopwatch.Start();
        for (int h = 0; h < threadCount; h++)
        {
            var handle = new ManualResetEvent(false);
            handles[h] = handle;

            var thread = new Thread(() =>
            {
                Runner(testFunc, repeatCount);
                handle.Set();
            });

            thread.Name = "Thread id" + h;
            thread.IsBackground = true;
            thread.Priority = ThreadPriority.Normal;

            thread.Start();
        }

        WaitHandle.WaitAll(handles);
        stopwatch.Stop();
        Console.WriteLine("All Threads Total time taken " + stopwatch.ElapsedMilliseconds);
    }

    private static void Runner(
        Action testFunc,
        int count
        )
    {
        //Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2); // Use only the second core 
        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.BelowNormal;
        Process.GetCurrentProcess().PriorityBoostEnabled = false;
        Thread.CurrentThread.Priority = ThreadPriority.Normal;

        var stopwatch = new Stopwatch();

        // warmup
        stopwatch.Start();
        while(stopwatch.ElapsedMilliseconds < 10000)
            testFunc();
        stopwatch.Stop();

        long elmsec = 0;
        for (int i = 0; i < count; i++)
        {
            stopwatch.Reset();
            stopwatch.Start();
            testFunc();
            stopwatch.Stop();

            elmsec += stopwatch.ElapsedMilliseconds;
            Console.WriteLine("Ticks: " + stopwatch.ElapsedTicks +
                            " mS: " + stopwatch.ElapsedMilliseconds + " Thread name: " + Thread.CurrentThread.Name);
        }

        Console.WriteLine("Total time taken " + elmsec + " Thread name: " + Thread.CurrentThread.Name);
    }

    /// <summary>
    /// Entry point
    /// </summary>
    /// <param name="args"></param>
    private static void Main(string[] args)

    {
        var files = GetFiles(args.FirstOrDefault());
        if (!files.Any())
        {
            Console.WriteLine("Source files were not found.");
            goto End;
        }           

        //// Run tests
        Console.WriteLine("ImageMagick run... Resize");
        Runner(() => PerformanceTests.RunResizeImageMagickTest(files[0]), 20);

        Console.WriteLine("Aurigma run... Resize");
        Runner(() => PerformanceTests.RunResizeAurigmaTest(files[0]), 20);

        Console.WriteLine("ImageMagick run... multi Resize");
        ThreadRunner(() => PerformanceTests.RunResizeImageMagickTest(files[0]), 20, 2);

        Console.WriteLine("Aurigma run... multi Resize");
        ThreadRunner(() => PerformanceTests.RunResizeAurigmaTest(files[0]), 20, 2);

    End:
        Console.WriteLine("Done");
        Console.ReadKey();
    }

    public static void RunResizeImageMagickTest(string source)
    {
        float[] ratios = { 0.25f, 0.8f, 1.4f };

        // load the source bitmap
        using (MagickImage bitmap = new MagickImage(source))
        {
            foreach (float ratio in ratios)
            {
                // determine the target image size
                var size = new Size((int)Math.Round(bitmap.Width * ratio), (int)Math.Round(bitmap.Height * ratio));

                MagickImage thumbnail = null;
                try
                {
                    thumbnail = new MagickImage(bitmap);

                    // scale the image down
                    thumbnail.Resize(size.Width, size.Height);
                }
                finally
                {
                    if (thumbnail != null && thumbnail != bitmap)
                    {
                        thumbnail.Dispose();
                    }
                }
            }
        }
    }

    public static void RunResizeAurigmaTest(string source)
    {
        float[] ratios = { 0.25f, 0.8f, 1.4f };

        //// load the source bitmap
        using (ABitmap bitmap = new ABitmap(source))
        {
            foreach (float ratio in ratios)
            {
                // determine the target image size
                var size = new Size((int)Math.Round(bitmap.Width * ratio), (int)Math.Round(bitmap.Height * ratio));

                ABitmap thumbnail = null;
                try
                {
                    thumbnail = new ABitmap();

                    // scale the image down
                    using (var resize = new Resize(size, InterpolationMode.HighQuality))
                    {
                        resize.ApplyTransform(bitmap, thumbnail);
                    }
                }
                finally
                {
                    if (thumbnail != null && thumbnail != bitmap)
                    {
                        thumbnail.Dispose();
                    }
                }
            }
        }
    }

添加了测试代码。我使用 C#/.NET,ImageMagick 通过 ImageMagick.Net 库工作,Aurigma 也有一个。对于 IM .net lib 是在 C++/CLI 上编写的,IM 是 C。使用了很多语言。

用于 IM 的 OpenMP 已关闭。

4

1 回答 1

0

可能是内存缓存问题。多个线程以某种方式利用内存可能会创建一个场景,即一个线程使另一个线程正在使用的缓存内存无效,从而导致停顿。

不是纯粹的数字运算,而是依赖大量 IO(CPU<->Memory)的程序更难分析。

于 2015-09-02T08:37:48.373 回答