C# 在 Java 中移位是否比乘法和除法更快?。网?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1168451/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 09:52:37  来源:igfitidea点击:

Is shifting bits faster than multiplying and dividing in Java? .NET?

c#java.netoptimizationbit-manipulation

提问by

Shifting bits left and right is apparently faster than multiplication and division operations on most, maybe even all, CPUs if you happen to be using a power of 2. However, it can reduce the clarity of code for some readers and some algorithms. Is bit-shifting really necessary for performance, or can I expect the compiler or VM to notice the case and optimize it (in particular, when the power-of-2 is a literal)? I am mainly interested in the Java and .NET behavior but welcome insights into other language implementations as well.

如果您碰巧使用 2 的幂,那么在大多数甚至所有 CPU 上左右移动位显然比乘法和除法运算快。但是,它会降低某些读者和某些算法的代码清晰度。移位对于性能来说真的是必要的,还是我可以期望编译器或 VM 注意到这种情况并对其进行优化(特别是当 2 的幂是文字时)?我主要对 Java 和 .NET 行为感兴趣,但也欢迎深入了解其他语言实现。

采纳答案by Michael Burr

Most compilers today will do more than convert multiply or divide by a power-of-two to shift operations. When optimizing, many compilers can optimize a multiply or divide with a compile time constant even if it's not a power of 2. Often a multiply or divide can be decomposed to a series of shifts and adds, and if that series of operations will be faster than the multiply or divide, the compiler will use it.

今天的大多数编译器所做的不仅仅是将乘法或除以二的幂转换为移位运算。优化时,许多编译器可以使用编译时间常数优化乘法或除法,即使它不是 2 的幂。通常乘法或除法可以分解为一系列移位和加法,如果这一系列操作会更快比乘法或除法,编译器会使用它。

For division by a constant, the compiler can often convert the operation to a multiply by a 'magic number' followed by a shift. This can be a major clock-cycle saver since multiplication is often much faster than a division operation.

对于常数除法,编译器通常可以将运算转换为乘以“幻数”,然后是移位。这可能是一个主要的时钟周期节省器,因为乘法通常比除法运算快得多。

Henry Warren's book, Hacker's Delight,has a wealth of information on this topic, which is also covered quite well on the companion website:

亨利·沃伦 (Henry Warren) 的书《黑客的喜悦》(Hacker's Delight)提供了大量关于此主题的信息,在配套网站上也有很好的介绍:

See also a discussion (with a link or two ) in:

另请参阅以下讨论(带有一两个链接):

Anyway, all this boils down to allowing the compiler to take care of the tedious details of micro-optimizations. It's been years since doing your own shifts outsmarted the compiler.

无论如何,这一切都归结为允许编译器处理微优化的繁琐细节。自从自己做轮班超过编译器以来,已经有很多年了。

回答by Greg Hewgill

You can almost certainly depend on the literal-power-of-two multiplication optimisation to a shift operation. This is one of the first optimisations that students of compiler construction will learn. :)

您几乎可以肯定地将文字乘法优化用于移位运算。这是编译器构造的学生将学习的首批优化之一。:)

However, I don't think there's any guarantee for this. Your source code should reflect your intent, rather than trying to tell the optimiser what to do. If you're making a quantity larger, use multiplication. If you're moving a bit field from one place to another (think RGB colour manipulation), use a shift operation. Either way, your source code will reflect what you are actually doing.

但是,我不认为对此有任何保证。您的源代码应该反映您的意图,而不是试图告诉优化器该做什么。如果您要使数量更大,请使用乘法。如果您要将位域从一个位置移动到另一个位置(想想 RGB 颜色操作),请使用移位操作。无论哪种方式,您的源代码都将反映您实际在做什么。

回答by Sam Harwell

If the compiler (compile-time constant) or JIT (runtime constant) knows that the divisor or multiplicand is a power of two and integer arithmetic is being performed, it will convert it to a shift for you.

如果编译器(编译时常量)或 JIT(运行时常量)知道除数或被乘数是 2 的幂并且正在执行整数算术,它将为您将其转换为移位。

回答by Savvas Dalkitsis

I am stunned as I just wrote this code and realized that shifting by one is actually slower than multiplying by 2!

当我刚刚写完这段代码时,我惊呆了,意识到移位 1 实际上比乘以 2 慢!

(EDIT: changed the code to stop overflowing after Michael Myers' suggestion, but the results are the same! What is wrong here?)

(编辑:在迈克尔迈尔斯的建议之后更改了代码以停止溢出,但结果是一样的!这里有什么问题?)

import java.util.Date;

public class Test {
    public static void main(String[] args) {
        Date before = new Date();
        for (int j = 1; j < 50000000; j++) {
            int a = 1 ;
            for (int i = 0; i< 10; i++){
                a *=2;
            }
        }
        Date after = new Date();
        System.out.println("Multiplying " + (after.getTime()-before.getTime()) + " milliseconds");
        before = new Date();
        for (int j = 1; j < 50000000; j++) {
            int a = 1 ;
            for (int i = 0; i< 10; i++){
                a = a << 1;
            }
        }
        after = new Date();
        System.out.println("Shifting " + (after.getTime()-before.getTime()) + " milliseconds");
    }
}

The results are:

结果是:

Multiplying 639 milliseconds
Shifting 718 milliseconds

乘以 639 毫秒
移位 718 毫秒

回答by Chris Brandsma

I would ask "what are you doing that it would matter?". First design your code for readability and maintainability. The likelyhood that doing bit shifting verses standard multiplication will make a performance difference is EXTREMELY small.

我会问“你在做什么这会很重要?”。首先设计代码的可读性和可维护性。进行位移位与标准乘法相比会产生性能差异的可能性非常小。

回答by Polaris878

Most compilers will turn multiplication and division into bit shifts when appropriate. It is one of the easiest optimizations to do. So, you should do what is more easily readable and appropriate for the given task.

大多数编译器会在适当的时候将乘法和除法转换为位移。这是最简单的优化之一。所以,你应该做更容易阅读和适合给定任务的事情。

回答by Jason Creighton

Almost any environment worth its salt will optimize this away for you. And if it doesn't, you've got bigger fish to fry. Seriously, do not waste one more second thinking about this. You will know when you have performance problems.And after you run a profiler, you will know what is causing it, and it should be fairly clear how to fix it.

几乎任何物有所值的环境都会为您优化它。如果没有,你就有更大的鱼要炸了。说真的,不要再浪费一秒钟思考这个问题。当您遇到性能问题时,您就会知道。在运行分析器之后,您将知道是什么导致了它,并且应该很清楚如何修复它。

You will never hear anyone say "my application was too slow, then I started randomly replacing x * 2with x << 1and everything was fixed!" Performance problems are generally solved by finding a way to do an order of magnitude less work, not by finding a way to do the same work 1% faster.

你永远不会听到有人说“我的应用程序太慢了,然后我开始随机替换x * 2x << 1一切都修复了!” 性能问题通常是通过找到一种方法来减少一个数量级的工作来解决的,而不是找到一种方法来将相同的工作速度提高 1%。

回答by Bill Crim

Humans are wrong in these cases.

在这些情况下,人类是错误的。

99% when they try to second guess a modern (and all future) compilers.
99.9% when they try to second guess modern (and all future) JITs at the same time.
99.999% when they try to second guess modern (and all future) CPU optimizations.

99% 当他们尝试再次猜测现代(以及所有未来)编译器时。
99.9% 当他们试图同时对现代(和所有未来)JIT 进行二次猜测时。
当他们尝试再次猜测现代(和所有未来)CPU 优化时,99.999%。

Program in a way that accurately describes what you want to accomplish, not how to do it. Future versions of the JIT, VM, compiler, and CPU can all be independantly improved and optimized. If you specify something so tiny and specific, you lose the benefit of all future optimizations.

以一种准确描述你想要完成的事情的方式进行编程,而不是如何去做。未来版本的 JIT、VM、编译器和 CPU 都可以独立改进和优化。如果您指定如此微小而具体的内容,您将失去所有未来优化的好处。

回答by Henk Holterman

It is hardware dependent. If we are talking micro-controller or i386, then shifting might be faster but, as several answers state, your compiler will usually do the optimization for you.

它依赖于硬件。如果我们谈论的是微控制器或 i386,那么移位可能会更快,但正如几个答案所述,您的编译器通常会为您进行优化。

On modern (Pentium Pro and beyond) hardware the pipelining makes this totally irrelevant and straying from the beaten path usually means you loose a lot more optimizations than you can gain.

在现代(Pentium Pro 及更高版本)硬件上,流水线使这完全无关紧要,并且偏离常规路径通常意味着您失去的优化比您可以获得的要多得多。

Micro optimizations are not only a waste of your time, they are also extremely difficult to get right.

微优化不仅浪费您的时间,而且也极难做好。

回答by Michael Burr

On computers I tested, integer divisions are 4 to 10 times slowerthan other operations.

在我测试过的计算机上,整数除法比其他运算慢 4 到 10 倍

When compilers may replace divisions by multiples of 2 and make you see no difference, divisions by not multiples of 2 are significantly slower.

当编译器可能会用 2 的倍数替换除法并且让您看不出区别时,除法不是 2 的倍数会显着变慢。

For example, I have a (graphics) program with many many many divisions by 255. Actually my computation is :

例如,我有一个(图形)程序,其中有许多除以 255 的除法。实际上我的计算是:

r = (((top.R - bottom.R) * alpha + (bottom.R * 255)) * 0x8081) >> 23;

I can ensure that it is a lot faster than my previous computation :

我可以确保它比我之前的计算快很多:

r = ((top.R - bottom.R) * alpha + (bottom.R * 255)) / 255;

so no, compilers cannot do all the tricks of optimization.

所以不,编译器不能做所有的优化技巧。