programing

Struct를 사용해야 하는 경우와OpenStruct?

yellowcard 2023. 6. 4. 10:26
반응형

Struct를 사용해야 하는 경우와OpenStruct?

일반적으로 Struct와 비교하여 OpenStruct를 사용할 때의 장점과 단점은 무엇입니까?어떤 유형의 일반적인 사용 사례가 각각 적합합니까?

와 함께OpenStruct임의로 속성을 만들 수 있습니다.aStruct그러나 속성을 생성할 때 속성이 정의되어 있어야 합니다.다른 것보다 하나를 선택하는 것은 주로 나중에 속성을 추가할 수 있는지 여부에 따라 결정되어야 합니다.

그들을 생각하는 방법은 한 쪽은 해시이고 다른 쪽은 계급 사이의 스펙트럼의 중간점입니다.그들은 데이터 간의 관계가 데이터보다 더 구체적이라는 것을 의미합니다.Hash수업처럼 인스턴스 메소드가 없어요예를 들어, 함수에 대한 여러 옵션은 해시에서 의미가 있습니다. 관련성이 거의 없습니다.기능에 필요한 이름, 이메일 및 전화 번호를 함께 패키지로 만들 수 있습니다.Struct또는OpenStruct이름, 전자 메일 및 전화 번호가 "첫 번째 성" 및 "마지막, 첫 번째" 형식으로 이름을 제공하는 방법이 필요한 경우 클래스를 만들어야 합니다.

기타 벤치마크:

require 'benchmark'
require 'ostruct'

REP = 100000

User = Struct.new(:name, :age)

USER = "User".freeze
AGE = 21
HASH = {:name => USER, :age => AGE}.freeze

Benchmark.bm 20 do |x|
  x.report 'OpenStruct slow' do
    REP.times do |index|
       OpenStruct.new(:name => "User", :age => 21)
    end
  end

  x.report 'OpenStruct fast' do
    REP.times do |index|
       OpenStruct.new(HASH)
    end
  end

  x.report 'Struct slow' do
    REP.times do |index|
       User.new("User", 21)
    end
  end

  x.report 'Struct fast' do
    REP.times do |index|
       User.new(USER, AGE)
    end
  end
end

스스로 실행하지 않고 벤치마크 결과에 대한 아이디어를 얻고 싶어하는 참을성 없는 사람들을 위해 위의 코드의 출력이 있습니다(MB Pro 2.4).GHz i7)

                          user     system      total        real
OpenStruct slow       4.430000   0.250000   4.680000 (  4.683851)
OpenStruct fast       4.380000   0.270000   4.650000 (  4.649809)
Struct slow           0.090000   0.000000   0.090000 (  0.094136)
Struct fast           0.080000   0.000000   0.080000 (  0.078940)

업데이트:

백만 개의 인스턴스를 생성하기 위한 시간:

0.357788 seconds elapsed for Class.new (Ruby 2.5.5)
0.764953 seconds elapsed for Struct (Ruby 2.5.5)
0.842782 seconds elapsed for Hash (Ruby 2.5.5)
2.211959 seconds elapsed for OpenStruct (Ruby 2.5.5)

0.213175 seconds elapsed for Class.new (Ruby 2.6.3)
0.335341 seconds elapsed for Struct (Ruby 2.6.3)
0.836996 seconds elapsed for Hash (Ruby 2.6.3)
2.070901 seconds elapsed for OpenStruct (Ruby 2.6.3)

0.936016 seconds elapsed for Class.new (Ruby 2.7.2)
0.453067 seconds elapsed for Struct (Ruby 2.7.2)
1.016676 seconds elapsed for Hash (Ruby 2.7.2)
1.482318 seconds elapsed for OpenStruct (Ruby 2.7.2)

0.421272 seconds elapsed for Class.new (Ruby 3.0.0)
0.322617 seconds elapsed for Struct (Ruby 3.0.0)
0.719928 seconds elapsed for Hash (Ruby 3.0.0)
35.130777 seconds elapsed for OpenStruct (Ruby 3.0.0) (oops!)

0.443975 seconds elapsed for Class.new (Ruby 3.0.1)
0.348031 seconds elapsed for Struct (Ruby 3.0.1)
0.737662 seconds elapsed for Hash (Ruby 3.0.1)
16.264204 seconds elapsed for SmartHash (Ruby 3.0.1)  (meh)
53.396924 seconds elapsed for OpenStruct (Ruby 3.0.1)  (oops!)

참조: 루비 3.0.0 버그 #18032는 버그가 아닌 기능이기 때문에 닫혔습니다.

인용문:

OpenStruct는 이제 "반패턴"으로 간주되므로 더 이상 OpenStruct를 사용하지 않는 것이 좋습니다.

[Ruby team] 성능보다 정확성을 우선시하여 Ruby 2.2와 유사한 솔루션으로 돌아왔습니다.


이전 답변:

Ruby 2.4.1 기준으로 OpenStruct와 Struct는 속도 면에서 훨씬 더 가깝습니다.https://stackoverflow.com/a/43987844/128421 을 참조하십시오.


완성도를 위해:구조 대.수업 대.해시 대.오픈 스트럭처

Ruby 1.9.2에서 burtlo와 유사한 코드 실행(4개 코어 중 1개 x86_64, 8GB RAM) [열을 정렬하도록 편집된 테이블]:

1 Mio Structs 생성: 1.43초, 219MB/90MB(virt/res)1개의 Mio 클래스 인스턴스 생성: 1.43초, 219MB/90MB(virt/res)1 Mio 해시 생성: 4.46초, 493MB / 364MB(virt/res)1 Mio OpenStructs 생성: 415.13초, 2464MB / 2.3GB(virt/res) # 해시보다 최대 100배 느림100K OpenStructs 생성: 10.96초, 369MB / 242MB(virt/res)

OpenStructs는 너무 느리고 메모리 집약적이며 대규모 데이터 세트에 적합하게 확장되지 않습니다.


결과를 재현하기 위한 스크립트는 다음과 같습니다.

require 'ostruct'
require 'smart_hash'

MAX = 1_000_000

class C; 
  attr_accessor :name, :age; 
  def initialize(name, age)
    self.name = name
    self.age = age
  end
end
start = Time.now
collection = (1..MAX).collect do |i|
  C.new('User', 21)
end; 1
stop = Time.now
puts "    #{stop - start} seconds elapsed for Class.new (Ruby #{RUBY_VERSION})"


s = Struct.new(:name, :age)
start = Time.now
collection = (1..MAX).collect do |i|
  s.new('User', 21)
end; 1
stop = Time.now
puts "    #{stop - start} seconds elapsed for Struct (Ruby #{RUBY_VERSION})"


start = Time.now
collection = (1..MAX).collect do |i|
  {:name => "User" , :age => 21}
end; 1
stop = Time.now
puts "    #{stop - start} seconds elapsed for Hash (Ruby #{RUBY_VERSION})"


start = Time.now
collection = (1..MAX).collect do |i|
  s = SmartHash[].merge( {:name => "User" , :age => 21} )
end; 1
stop = Time.now
puts "    #{stop - start} seconds elapsed for SmartHash (Ruby #{RUBY_VERSION})"


start = Time.now
collection = (1..MAX).collect do |i|
  OpenStruct.new(:name => "User" , :age => 21)
end; 1
stop = Time.now
puts "    #{stop - start} seconds elapsed for OpenStruct (Ruby #{RUBY_VERSION})"

두 가지의 사용 사례는 상당히 다릅니다.

1는 Ruby 1.9 Struct와 .struct다의 선언인 루비Struct.new필드 이름 집합을 인수로 사용하고 새 클래스를 반환합니다.에서 a 찬가지로에, C서마, astruct선언은 필드 집합을 사용하고 프로그래머가 내장된 유형과 마찬가지로 새로운 복합 유형을 사용할 수 있도록 합니다.

루비:

Newtype = Struct.new(:data1, :data2)
n = Newtype.new

C:

typedef struct {
  int data1;
  char data2;
} newtype;

newtype n;

OpenStruct 클래스는 C의 익명 구조 선언과 비교할 수 있습니다.프로그래머가 복잡한 유형의 인스턴스를 만들 수 있습니다.

루비:

o = OpenStruct.new(data1: 0, data2: 0) 
o.data1 = 1
o.data2 = 2

C:

struct {
  int data1;
  char data2;
} o;

o.data1 = 1;
o.data2 = 2;

다음은 몇 가지 일반적인 사용 사례입니다.

OpenStructs를 사용하여 해시를 모든 해시 키에 응답하는 일회성 개체로 쉽게 변환할 수 있습니다.

h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2

구조체는 단축형 클래스 정의에 유용할 수 있습니다.

class MyClass < Struct.new(:a,:b,:c)
end

m = MyClass.new
m.a = 1

OpenStructs는 훨씬 더 많은 메모리를 사용하고 Structs에 비해 성능이 느립니다.

require 'ostruct' 

collection = (1..100000).collect do |index|
   OpenStruct.new(:name => "User", :age => 21)
end

내 시스템에서 다음 코드는 14초 만에 실행되었고 1.5GB의 메모리를 소비했습니다.마일리지는 다음과 같이 다를 수 있습니다.

User = Struct.new(:name, :age)

collection = (1..100000).collect do |index|
   User.new("User",21)
end

거의 즉각적으로 완료되었으며 26.6MB의 메모리를 사용했습니다.

Struct:

>> s = Struct.new(:a, :b).new(1, 2)
#=> #<struct a=1, b=2>
>> s.a
#=> 1
>> s.b
#=> 2
>> s.c
#=> NoMethodError: undefined method `c` for #<struct a=1, b=2>

OpenStruct:

>> require 'ostruct'
#=> true
>> os = OpenStruct.new(a: 1, b: 2)
#=> #<OpenStruct a=1, b=2>
>> os.a
#=> 1
>> os.b
#=> 2
>> os.c
#=> nil

새로운 방법과 관련하여 API를 살펴보십시오.그곳에서 많은 차이점을 발견할 수 있습니다.

개인적으로, 저는 OpenStruct를 꽤 좋아합니다. 왜냐하면 저는 객체의 구조를 미리 정의할 필요가 없고 제가 원하는 대로 추가할 수 있기 때문입니다.그것이 그것의 주요 장점이 될 것 같습니다.

@Robert 코드를 사용하여 해시를 추가합니다.: 벤치마크 항목에 매쉬하여 다음과 같은 결과를 얻었습니다.

                           user     system      total        real
Hashie::Mash slow      3.600000   0.000000   3.600000 (  3.755142)
Hashie::Mash fast      3.000000   0.000000   3.000000 (  3.318067)
OpenStruct slow       11.200000   0.010000  11.210000 ( 12.095004)
OpenStruct fast       10.900000   0.000000  10.900000 ( 12.669553)
Struct slow            0.370000   0.000000   0.370000 (  0.470550)
Struct fast            0.140000   0.000000   0.140000 (  0.145161)

실제로 질문에 대한 대답이 아니라 성능에 관심이 있다면 매우 중요한 고려 사항입니다.다음을 생성할 때마다OpenStruct이 작업은 메서드 캐시를 지웁니다. 즉, 응용 프로그램의 성능이 저하됩니다. OpenStructhttps://github.com/charliesome/charlie.bz/blob/master/posts/things-that-clear-rubys-method-cache.md#openstructs 은 단순히 어떻게 작동하는지에 대한 것이 아니라, 이를 사용하는 것이 전체 애플리케이션에 미치는 영향에 대한 것입니다.

언급URL : https://stackoverflow.com/questions/1177594/when-should-i-use-struct-vs-openstruct

반응형