【问题标题】:Howto make image generation scalable on Java?如何在 Java 中使图像生成具有可扩展性?
【发布时间】:2011-04-24 18:14:12
【问题描述】:

我正在尝试提高在 Linux 上运行的网络应用程序中验证码图像渲染的性能。查看目前使用的,发现瓶颈在于Java2D的使用,特别是Graphics2D类。

问题不在于执行速度,而在于可扩展性。基本上它不会缩放。在 1 个线程或 2 个线程中绘制验证码图像在执行时间方面没有任何改进。

例如,您可以查看以下为验证码图像创建背景的类。问题出现在对 Graphics2D::setColor() 和 Graphics2D::drawLine() 的调用中:

http://www.docjar.com/html/api/com/octo/captcha/component/image/backgroundgenerator/FunkyBackgroundGenerator.java.html

经过一番谷歌搜索,我发现一个主题说 Java2d 在多线程方面不是特别好(抱歉,不允许提供多个链接:) 但是,如果谷歌搜索“java2d 多线程”,您可以轻松找到该主题,这将是第一个结果)

我相信一定有一些库提供绘图功能而不使用 Java2d,但找不到它:( 或者 Java2d,可能,可以切换到某种模式,它不会阻止访问图形对象(顺便说一句,无头模式无济于事)。

我将不胜感激任何建议。预先感谢您的回答。

【问题讨论】:

  • FWIW 谷歌搜索时我得到的第一个打击是关于从多个线程写入相同的 graphics2D 对象。不是每个人都有自己的情况。
  • forums.sun.com/thread.jspa?threadID=5415900 这是引文 - “经过一番谷歌搜索,我发现这篇四年前的文章谈论 java 的 OGL 管道只允许在单线程上渲染(从 java 1.6 开始)。”

标签: java performance graphics scalability


【解决方案1】:

不会有一种快速的方法来分享可预测的Graphics2D,因为除非您有办法在每个像素上同步和重新排序,否则这将是一个巨大的竞争条件。

无论如何,您的 Graphics2DBufferedImage 支持,所以这可能是让您放慢速度的原因。这是一个非加速表面,所以绘图总是很慢。如果你的渲染服务器有它的图形硬件(它真的应该用于这样的应用程序),你可以使用VolatileImage,根据我的经验,它比BufferedImage 快一个或两个数量级。

否则,你必须将你的背景生成分割成一个网格,AffineTransform 让它们排列整齐,通过播种使所有网格元素的“随机性”通用,然后将它们缝合在一起,然后希望copyArea(...) 方法足够快,可以为您带来改进。我几乎会说这是一个杂牌,硬件加速是要走的路。

您还应该考虑离线预渲染大量它们,并根据需要提供它们。这样一来,性能或多或少不是问题,除非您在服务器空闲时间无法满足需求(在这种情况下,您需要新的硬件,并且应该只制作一个硬件加速渲染框)。

【讨论】:

  • 感谢您的回答并感谢您的建议。我会尝试使用它们来让它稍微快一点。但是看看源代码,我已经给出了一个链接。这些在 Graphics 对象上调用的方法除了将彩色像素放置在位置之外没有做任何事情。在图形硬件上加速这样的事情听起来很可笑。另一点是服务器在一些刀片服务器上运行,我什至不知道那个盒子的地理位置,它甚至可能根本不是一个物理盒子。
  • 理想的解决方案是一个库,它可以处理没有显卡的图像,但找不到它:(
  • @Stas:即使在点绘图的情况下,VolatileImages 通常也会比 BufferedImage 快得多,因为它存储在视频内存中,并且所有绘图调用都在幕后转换为硬件加速调用。不要低估现代 GPU 的真正速度,尤其是当 setColor() 和 drawLine() 是您的瓶颈时。您还可以从表示颜色值的整数数组中创建一个 BufferedImage 并查看这是否加快了速度。至少并行化应该是微不足道的。
  • 好啊,我摆脱了图形并用 WritableRaster 代替了它。将您的答案标记为该问题的答案,因为您已促使我想到这样做(带有颜色值的数组)。谢谢!
【解决方案2】:

基于简要了解您的代码的一些优化想法:

  • 您正在为每个验证码创建一个新的 BufferedImage。我认为您最好在 ThreadLocal 变量中为每个线程保留一个 BufferedImage 和 Graphics2D 并在创建新验证码时覆盖之前的验证码
  • 您正在对每个像素进行大循环,每个像素都需要进行大量计算。您希望绝对最小化在此循环中间完成的计算,例如在循环外进行“colorRightDown.getRed() / 255.0f”等的不断计算
  • 考虑将所有浮点计算转换为等效的定点整数数学。如果您可以使所有内容都适合整数,这通常会稍微快一些。
  • 使用带有整数颜色值的 BufferedImage.setRGB(),而不是带有新颜色的 Graphics2D.setColor - 它会快得多,并为您节省大量 GC 压力
  • 看看你能不能减少每像素的随机数调用次数,我数了每像素 7 次....你能不能少一点?您最好创建一个随机整数并测试位的子集。
  • 在您的内部 (i) 循环中使用宽度而不是 getImageWidth(),否则您将不必要地为每个像素调用 getImageWidth。 (j) 循环也是如此,尽管它的重要性要小得多。

我的猜测是,上述组合所带来的好处远远超过在问题上投入额外的处理器..... :-)

【讨论】:

    猜你喜欢
    • 2015-07-24
    • 1970-01-01
    • 2019-06-30
    • 2021-09-26
    • 2022-07-18
    • 1970-01-01
    • 1970-01-01
    • 2019-09-01
    • 1970-01-01
    相关资源
    最近更新 更多