Hi Marshall,

Yes, that's the gist of it.  If you look at the bytecode for the
defrecord, you should see something like:

  // Method descriptor #287 ()I

  // Stack: 1, Locals: 1

  public int hashCode();
    0  aload_0 [this]
    1  checkcast clojure.lang.IPersistentMap [18]
    4  invokestatic
clojure.lang.APersistentMap.mapHash(clojure.lang.IPersistentMap) : int
[303]
    7  ireturn
      Line numbers:
        [pc: 0, line: 1]
        [pc: 4, line: 1]
      Local variable table:
        [pc: 0, pc: 7] local: this index: 0 type: user.R1

  // Method descriptor #305 (Ljava/lang/Object;)Z
  // Stack: 3, Locals: 2

  public boolean equals(java.lang.Object G__1432);
     0  aload_0 [this]
     1  checkcast clojure.lang.IPersistentMap [18]
     4  aload_1 [G__1432]
     5  aconst_null
     6  astore_1 [G__1432]
     7  invokestatic
clojure.lang.APersistentMap.mapEquals(clojure.lang.IPersistentMap,
java.lang.Object) : boolean [309]
    10  ireturn

which calls the code at [1] and [2]. The bytecode I saw for deftype
did not override either, so you should get the default from Object
[3][4], which just does comparison by identity for equals, and the
default hashing method.  (Uses a native code call in what I'm seeing
for java.lang.Object's Java source via Netbeans, and javadocs say it
hashes the pointer address as you mentioned).

I'd say your assessment about hashing/identity and performance is
correct in regards to defrecord/deftype. On the other hand, it's good
to have correct value-based hashing out of the box with defrecord,
IMO.  I guess the choice to use one or the other should keep the
equals/hashCode stuff in mind, and whether you need things to be
value-based or not.

As a side note, as an experiment I just tried overriding Object.equals
and Object.hashCode with a defrecord, and I got a compiler error:

CompilerException java.lang.ClassFormatError: Duplicate method
name&signature in class file user/R1,
compiling:(/private/var/folders/0k/xj_drd990xxf4q99n2bdknrc0000gn/T/form-init3397614882621384237.clj:1:1)

I'm assuming that defrecord is adding its hashCode and equals
implementations without checking if it's being overridden, but haven't
verified. (That was with 1.7.0-beta3 at least).

Cheers!
steven




[1] - 
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java#L103-L112
[2] - 
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java#L52-L71
[3] - http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode()
[4] - 
http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object)


On Tue, Jun 2, 2015 at 11:28 PM, Mars0i <marsh...@logical.net> wrote:
> Steven--Oops, yeah, I am calling Java methods that are probably using
> hashCode(), and maybe equals().  Interesting.
>
> Is the idea that records are hashed on the values in their fields, while
> deftype object are just hashed a pointer (or whatever identical?
> uses)--something like that?  So if you want fast hashing by raw identity,
> you should not use defrecord?
>
>
>
> On Tuesday, June 2, 2015 at 8:10:04 PM UTC-5, Steven Yi wrote:
>>
>> I'm not aware of anything that would lead to significant differences in
>> regards to the function call.  As a test, I defined an interface and used
>> deftype and defrecord, then used no.disassemble[1] to inspect the results,
>> using the following commands:
>>
>> user=> (use 'no.disassemble)
>>
>> user=> (definterface P (testfn []))
>>
>> user=> (defrecord R1 [x y] P (testfn [this] (reset! x (inc @y)) (reset! y
>> (inc @x))))
>>
>> user=> (deftype R2 [x y] P (testfn [this] (reset! x (inc @y)) (reset! y
>> (inc @x))))
>>
>> user=> (println (disassemble R1))
>>
>> user=> (println (disassemble R2))
>>
>> Looking at the bytecode for the testfn() function for both R1 and R2,
>> they're pretty much identical (same opcodes).
>>
>> My hunch is that your performance issues may lie elsewhere.
>>
>> Just a random guess, are you by chance doing anything with the record/type
>> that would require calling the equals() or hashCode() functions?  defrecord
>> would be doing a bit more work there than deftype, which gets the default
>> equals/hashcode.
>>
>> Hope that helps!
>> steven
>>
>>
>> [1] - https://github.com/gtrak/no.disassemble/
>>
>> On Tuesday, June 2, 2015 at 1:08:13 PM UTC-4, Mars0i wrote:
>>>
>>> I have an application using Java interop, in which I can define a class
>>> using either deftype or defrecord.  It has one field, containing an atom
>>> containing a double, and a few methods.  One of the methods is specified by
>>> an interface defined in Java, and that method is called from Java.  This
>>> method is called many times in an inner loop.  I don't use any of the extra
>>> functionality provided by defrecord, but in otheri, similar programs, I
>>> will.
>>>
>>> When I use defrecord, the speed of the program is about 25% of its speed
>>> when I use deftype.  This is a one-line change.  I'm testing speed simply by
>>> running the program for a long time, and timing it.  I've done this test
>>> repeatedly, so there's no reason to think the differences have to do with
>>> other processes running on my machines.
>>>
>>> When I benchmark simple uses of defrecord and deftype using criterium,
>>> they're about the same speed.
>>>
>>> Is there any obvious reason to think that there are situations--e.g.
>>> method calls from Java--in which deftype would be expected to be
>>> significantly faster than defrecord?
>>>
>>> I can construct a minimal example based on my program and post it here,
>>> but I thought I'd check first whether there is something obvious that I
>>> don't get.
>>>
>>> [The test I did using defrecord is below.  I also replaced 'defrecord'
>>> with 'deftype' and did the same test.  I would think that the JIT compiler
>>> wouldn't be smart enough to optimize away whatever differs between defrecord
>>> and deftype, but maybe I'm wrong about that.
>>>
>>> (defrecord R [x y]
>>>    P
>>>    (incx2y [this] (reset! x (inc @y)))
>>>    (decy2x [this] (reset! y (dec @x))))
>>>
>>> (def r (R. (atom 2) (atom 1)))
>>>
>>> (bench (def _ (dotimes [_ 50000000] (incx2y r) (decy2x r) r)))
>>> ]
>>>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to a topic in the
> Google Groups "Clojure" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/clojure/EdjnSxRkOPk/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to